Railway Operation Simulator  v2.15.0
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand() & random()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 //#include "DisplayUnit.h" included in TrackUnit.h
49 #include "PerfLogUnit.h"
50 #include "Utilities.h"
51 
52 // ---------------------------------------------------------------------------
53 #pragma package(smart_init)
54 
56 
57 // ---------------------------------------------------------------------------
58 
59 int TTrain::NextTrainID = 0; // has to be initialised outside the class
60 
61 // ---------------------------------------------------------------------------
62 
63 TExitInfo::TExitInfo() //default constructor
64 {
65  ServiceReference = " ";
66  RepeatNumber = 0;
67  TimeToExitSecs = -1;
68 }
69 
70 // ---------------------------------------------------------------------------
71 
72 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
73  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
74  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
75  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
76  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
77  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
78 /*
79  Construct a new train with general default values and input values for position and headcode.
80  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
81  This is because trains are kept in a vector and vectors erase elements during internal operations.
82  Deletion is explicit by using a special function. Increment the static class member NextTrainID
83  after setting this train's ID.
84 */
85 
86 {
87  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
88  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
89  AnsiString(TrainModeIn));
90  // AutoControl = true;//all trains start in auto control
91  UpdateCounter = 0;
92  TimeTimeLocArrived = false;
93  Derailed = false;
94  DerailPending = false;
95  Crashed = false;
96  StoppedAtBuffers = false;
97  StoppedAtSignal = false;
98  StoppedAtLocation = false;
99  StoppedAfterSPAD = false;
100  StoppedWithoutPower = false; // new at v2.4.0
101  StoppedForTrainInFront = false;
102  SignallerStoppingFlag = false;
103  SignallerStopped = false;
104  SignallerRemoved = false;
105  NotInService = false;
106  HoldAtLocationInTTMode = false;
107  AllowedToPassRedSignal = false;
108  CallingOnFlag = false;
109  BeingCalledOn = false;
110  DepartureTimeSet = false;
112  TimetableFinished = false;
113  LastActionDelayFlag = false;
114  OneLengthAccelDecel = false;
115  TrainCrashedInto = -1;
117  Plotted = false;
118  TrainGone = false;
119  SPADFlag = false;
120  FrontCodePtr = new Graphics::TBitmap;
121  FrontCodePtr->PixelFormat = pf8bit;
122  FrontCodePtr->Height = 8;
123  FrontCodePtr->Width = 8;
125  FrontCodePtr->Transparent = false;
126  AValue = sqrt(2 * PowerAtRail / Mass);
128  TerminatedMessageSent = false;
129  JoinedOtherTrainFlag = false;
131  FollowOnServiceRef = ""; //added at v2.12.0
132  TreatPassAsTimeLocDeparture = false; //added at v2.12.0
133  StepForwardFlag = false;
135  for(int x = 0; x < 4; x++)
136  {
137  HeadCodeGrPtr[x] = new Graphics::TBitmap;
138  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
139  HeadCodeGrPtr[x]->Height = 8;
140  HeadCodeGrPtr[x]->Width = 8;
142  HeadCodeGrPtr[x]->Transparent = false;
143  }
144  for(int x = 0; x < 4; x++)
145  {
146  BackgroundPtr[x] = new Graphics::TBitmap;
147  BackgroundPtr[x]->PixelFormat = pf8bit;
148  BackgroundPtr[x]->Height = 8;
149  BackgroundPtr[x]->Width = 8;
151  BackgroundPtr[x]->Transparent = false;
152  }
153  for(int x = 0; x < 4; x++)
154  {
156  // set here to ensure have values
157  }
158  for(int x = 0; x < 4; x++)
159  {
160  PlotElement[x] = -1; // marker for not plotted yet
161  }
162  for(int x = 0; x < 3; x++)
163  {
164  OldZoomOutElement[x] = -1; // marker for not plotted yet
165  }
167  NextTrainID++;
168 
169  // new values added to complete initialisation of all TTrain variables
170 
171  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
172  // TrainDataEntryPtr, initialise in AddTrain
174  FrontElementLength = 0;
175  EntrySpeed = 0;
176  ExitSpeedHalf = 0;
177  ExitSpeedFull = 0;
178  MaxExitSpeed = 0;
179  BrakeRate = 0;
181  FirstHalfMove = true;
182  EntryTime = 0;
183  ExitTimeHalf = 0;
184  ExitTimeFull = 0;
185  ReleaseTime = 0;
186  TRSTime = 0;
187  LastActionTime = 0;
188  Straddle = MidLag;
189  LeadElement = -1;
190  LeadEntryPos = 0;
191  LeadExitPos = 0;
192  MidElement = -1;
193  MidEntryPos = 0;
194  MidExitPos = 0;
195  LagElement = -1;
196  LagEntryPos = 0;
197  LagExitPos = 0;
198  TrainFailed = false; // added at v2.4.0
199  for(int x = 0; x < 4; x++)
200  {
201  HOffset[x] = 0;
202  VOffset[x] = 0;
203  PlotEntryPos[x] = 0;
204  }
205  OpTimeToAct = 60; // default value, new at v2.2.0
206  TimeToExit = -1;
207  ExitPair.first = -1;
208  ExitPair.second = -1;
209  MinsDelayed = 0.0; // new at v2.2.0
210  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
211  FinishJoinLogSent = false;
212  // added at v2.4.0 to prevent repeatdly logging the event
215  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
219  ZeroPowerNoCDTMessage = false;
224  TrainFailurePending = false;
225  SkippedDeparture = false;
226  ActionsSkippedFlag = false;
227  SkipPtrValue = 0;
228  TrainSkippedEvents = 0;
229  DelayedRandMins = 0; //added at v2.13.0
230  NewDelay = 0; //added at v2.13.0
231  CumulativeDelayedRandMinsOneTrain = 0; //added at v2.13.0
232  ActualArrivalTime = TDateTime(0); //added at v2.13.0
233  LastSigPassedFailed = false; //added at v2.13.0
234  Utilities->CallLogPop(648);
235 }
236 
237 // ---------------------------------------------------------------------------
238 
239 void TTrain::DeleteTrain(int Caller)
240 /*
241  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
242  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
243  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
244  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
245  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
246  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
247  No need to delete HeadCodePosition as that just points to existing bitmaps
248 */{
249  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
250  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
251  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
252  if(Display->ZoomOutFlag)
253  {
255  }
256  if(FrontCodePtr == 0)
257  {
258  throw Exception("Error in attempting to delete FrontCodePtr");
259  }
260  delete FrontCodePtr;
261  FrontCodePtr = 0;
262  for(int x = 0; x < 4; x++)
263  {
264  if(BackgroundPtr[x] == 0)
265  {
266  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
267  }
268  delete BackgroundPtr[x];
269  BackgroundPtr[x] = 0;
270  }
271  for(int x = 0; x < 4; x++)
272  {
273  if(HeadCodeGrPtr[x] == 0)
274  {
275  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
276  }
277  delete HeadCodeGrPtr[x];
278  HeadCodeGrPtr[x] = 0;
279  }
280  Utilities->CallLogPop(649);
281 }
282 
283 // ---------------------------------------------------------------------------
284 
286 /*
287  Plots the train starting position on screen. Note that the check for starting on straight points &
288  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
289  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
290  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
291  Set the headcode graphics pointers from the headcode text, then check whether starting at a
292  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
293  for the continuation element. Otherwise set Lead and Mid values,
294 
295  and Lead element value unless
296  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
297  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
298  then check if a train on either Mid or Lag and if so give a warning message and return false so
299  that the calling function can delete the train. Plot the Mid element train values then do similarly
300  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
301  the train. Finally set the Plotted flag and return true.
302 */{
303  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
304  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
305 
307  // PlotStartTime = TrainController->TTClockTime;
308  FirstHalfMove = true;
309 
310  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
311  // 'claim' it for this train to prevent any other waiting trains trying to enter
313  {
314  LagElement = -1; // not to be plotted
315  LagExitPos = 0; // not to be plotted
316  LagEntryPos = 0; // not to be plotted
317  MidElement = -1; // not to be plotted
318  MidExitPos = 0; // not to be plotted
319  MidEntryPos = 0; // not to be plotted
321  LeadExitPos = 1; // will be 1 for continuation entry
322  LeadEntryPos = 0;
323 
325  MaxExitSpeed = StartSpeed; // initial value
327  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
328  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
329  if(EntrySpeed > SpeedLimit)
330  {
331  EntrySpeed = SpeedLimit;
332  }
334  {
336  }
338  // LeadElement is the element to be entered
339 
340  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
341  // can achieve ExitSpeedFull at the half braking rate.
343  {
344  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
345  if(TempEntrySpeed < EntrySpeed)
346  {
347  EntrySpeed = TempEntrySpeed;
349  }
350  }
351  Straddle = MidLag; // only for starting on a continuation
353  // no need to stop gap flashing if start on continuation
354  }
355  else // not starting at a continuation
356  {
357  LagElement = -1;
358  LagEntryPos = 0;
359  LagExitPos = 0;
366 
368  MaxExitSpeed = StartSpeed; // initial value
370  bool TempDerail = false; // dummy
371  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
373  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
374  {
375  StoppedWithoutPower = true;
376  }
377  // facing buffers check - ignore starting speed if start facing buffers
378  StoppedAtBuffers = false;
379  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
382  {
383  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
384  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
385  EntrySpeed = 0;
386  ExitSpeedHalf = 0;
387  ExitSpeedFull = 0;
388  MaxExitSpeed = 0;
389  // SetTrainMovementValues not called so set this here
390  BrakeRate = 0;
393  StoppedAtSignal = false;
394  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
395  // signal check is an 'else'
396  if(!StoppedAtLocation)
397  {
398  StoppedAtBuffers = true; // stopped at location takes precedence
399  }
400  }
401 
402  // facing continuation check - don't allow to stop even if no power
404  {
405  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
406  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
410  BrakeRate = 0;
411  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
412  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
413  }
414 
415  // Signal check
416  else if((NextElementPosition > -1) && (NextEntryPos > -1))
417  // condition check added as precaution after SloughIECC error reported by James U
418  {
419  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
420  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
421  {
422  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
423  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
424  EntrySpeed = 0;
425  ExitSpeedHalf = 0;
426  ExitSpeedFull = 0;
427  MaxExitSpeed = 0;
428  BrakeRate = 0;
431  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
432  {
433  StoppedAtSignal = true;
435  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
436  }
438  {
439  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass stop signal' is offered in popup menu rather than move
440  // forwards, but don't change the background colour so still shows as stopped at location
441  StoppedAtSignal = true;
442  }
443  }
444  else
445  {
446  StoppedAtSignal = false;
447  if(NextEntryPos > 1)
448  {
449  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
450  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
451  }
452  else
453  {
454  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
455  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
456  }
457  if(EntrySpeed > SpeedLimit)
458  {
459  EntrySpeed = SpeedLimit;
460  }
462  {
464  }
466  TDateTime TestTime = TrainController->TTClockTime; // test
467  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
468  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
469  // NextElement is the element to be entered
470 
471  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
472  // can achieve ExitSpeedFull at the half braking rate.
474  {
475  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
476  // half braking
477  if(TempEntrySpeed < EntrySpeed)
478  {
479  EntrySpeed = TempEntrySpeed;
480  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
481  }
482  }
483  }
484  }
486  {
487  throw Exception("Error, LeadElement Exit Connection is NotSet");
488  }
489  }
490  if(MidElement > -1) // will be -1 if start on continuation
491  {
492  Straddle = LeadMid;
496  {
497  for(int x = 0; x < 4; x++)
498  {
499  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
500  }
501  }
502  else
503  {
504  for(int x = 0; x < 4; x++)
505  {
507  }
508  }
509  if(TrainMode == Timetable)
510  {
512  }
513  else
514  {
516  }
518  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
519 
522 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
523  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
524  {
525  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
526  Utilities->CallLogPop(651);
527  return false;
528  }
529 */
534  PlotTrainGraphic(8, 0, Display);
535  PlotTrainGraphic(9, 1, Display);
536 
539 
540  // pick up background bitmaps [2] & [3]
541 
544 
545  PlotElement[2] = MidElement;
547  PlotElement[3] = MidElement;
549  PlotTrainGraphic(10, 2, Display);
550  PlotTrainGraphic(11, 3, Display);
551  // Plotted = true; set in PlotTrainGraphic
552  }
553  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
554  Utilities->CallLogPop(652);
555 }
556 
557 // ---------------------------------------------------------------------------
558 void TTrain::UnplotTrain(int Caller)
559 {
560  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
561  if(!Plotted)
562  {
563  return;
564  }
565  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
566 
567  if(Straddle == MidLag)
568  {
569  if(MidElement > -1)
570  {
575  // to force plot of locked route marker, needed once only for the element
576  }
577  if(LagElement > -1)
578  {
583  // to force plot of locked route marker, needed once only for the element
584  }
585  }
586  else if(Straddle == LeadMidLag)
587  {
588  if(LeadElement > -1)
589  {
592  // to force plot of locked route marker, needed once only for the element
593  }
594  if(MidElement > -1)
595  {
600  // to force plot of locked route marker, needed once only for the element
601  }
602  if(LagElement > -1)
603  {
606  // to force plot of locked route marker, needed once only for the element
607  }
608  }
609  else if(Straddle == LeadMid)
610  {
611  if(LeadElement > -1)
612  {
617  // to force plot of locked route marker, needed once only for the element
618  }
619  if(MidElement > -1)
620  {
625  // to force plot of locked route marker, needed once only for the element
626  }
627  }
628  if(LeadElement > -1)
629  {
631  }
632  if(MidElement > -1)
633  {
635  }
636  if(LagElement > -1)
637  {
639  }
640  Plotted = false;
642  Display->Update();
643  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
644  // resurrected when Update() dropped from PlotOutput etc
645  Utilities->CallLogPop(653);
646 }
647 
648 // ----------------------------------------------------------------------------
649 
650 void TTrain::UpdateTrain(int Caller)
651 /*
652  Note: Some changes made since comments written
653 
654  Brief:
655  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
656  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
657  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
658  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
659  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
660  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
661  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
662  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
663  changed to MidLag within the function and all elements moved down one, old Mid becomes
664  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
665  incremented to reflect the position the train now occupies.
666 
667  Detail:
668  Set TrainFailurePending if all conditions met
669  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
670  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
671  and return.
672  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
673  If there's a LagElement (there will be but include check for good practice - next
674  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
675  train fully on offending point - Derail set and DerailPanding reset, train background
676  colour changed (note that BackgroundColour is a property of the train itself) then return.
677  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
678  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
679  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
680  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
681  if LeadElement is a fouled trailing point.
682  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
683  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
684  replotting the last background segment and checking whether the element is a bridge or crossover with the other
685  track in a route, in which case the route colour is replotted.
686  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
687  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
688  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
689  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
690  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
691  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
692  train can be deleted by the calling function, and the function returns.
693  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
694  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
695  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
696  basic red aspect.
697 
698  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
699  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
700  regardless of direction, and with the correct front code colour.
701 
702  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
703  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
704  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
705  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
706  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
707  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
708  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
709 
710  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
711  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
712  changed similarly. The function then returns.
713 
714  If Crashed is not set then Straddle is incremented and the function returns.
715 */
716 
717 {
718  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
719  UpdateCounter++;
720  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
721  if(UpdateCounter >= 100)
722  {
723  UpdateCounter = 0;
724  }
725  int RandRange = (TrainController->MTBFHours * 3600) / 53;
726 
727  // MTBFHours is in timetable clock hours, min value is 1 & max value is 9,999 (integer values on input)
728  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
729  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
730  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
731  // RandomFailureCounter value is fixed for a full cycle of train updates so this
732  // makes sure there's no bunching of failures as there is for a fixed comparison number
733  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
734  // gives a random number between 0 and 16384 (defined as RAND_MAX in stdlib.h)
735  {
736  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
737  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
738  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
739  // don't fail if:
740  // (a) on a continuation (entering or leaving);
741  // (b) already failed;
742  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
743  // (d) train terminated;
744  // (e) crashed or derailed; or
745  // (f) under signaller control and stopped.
746  // (g) TreatPassAsTimeLocDeparture is true //added at v2.12.0
747  {
748  if((random(RandRange) == 0) && !TreatPassAsTimeLocDeparture)
749  // max value for RandRange is over 2x10^9
750  {
751  // here if failure due
752  TrainFailurePending = true;
753  // fail when PlotElements set to proper Lead & Mid Elements
754  }
755  }
756  }
757 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
758  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
759  {
760  StoppedWithoutPower = true;
761  }
762 */
763 // float TimeToExit; //added at v2.10.0 Removed these so original values retained - used when train on continuation
764 // THVShortPair ExitPair; //added at v2.10.0
765  int LockedVectorNumber;
766  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
767  // default values - these needed for route checker below
768  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
769 
771  {
773  }
774  if(Crashed || Derailed)
775  {
777  {
778  PlotTrain(7, Display);
779  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
780  Display->Update();
781  }
782  OpTimeToAct = 0.0;
783  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
784  Utilities->CallLogPop(1017);
785  return; // no further action, user has to remove or work around
786  }
788  {
790  }
792  {
794  }
795  if(StoppedWithoutPower && (TrainMode == Signaller)) //added at v2.13.2 as this condition not covered - was shown as normal
796  {
798  }
800  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
801  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
802  // to move & then stop again at the same station
803  {
804  TimeTimeLocArrived = false;
805  }
806  if(!Stopped() && !SPADFlag && !TrainFailed)
807  {
809  }
810  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
811  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
812 /* old version where force a stop at buffers regardless of speed
813  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
814  else StoppedAtBuffers = false;
815 */
816 
817  // new version where crash if run into buffers
818  if(!Crashed)
819  {
820  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
821  {
822  if(ExitSpeedFull > 1)
823  {
824  Crashed = true;
828  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
829  // no need for missed action logs - will be sent when train removed
830  StoppedAtBuffers = false;
831  }
833  // stopped at location & stopped without power take precedence
834  {
835  StoppedAtBuffers = true;
836  }
837  else
838  {
839  StoppedAtBuffers = false;
840  }
841  }
842  else
843  {
844  StoppedAtBuffers = false;
845  }
846  }
847  else
848  {
849  StoppedAtBuffers = false;
850  }
851  // if crashed don't want stopped at buffers set
852 
853  // also crash if run into a level crossing that is changing or has barriers up
854  if(!Crashed)
855  {
856  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
857  {
858  int H = Track->TrackElementAt(873, LeadElement).HLoc;
859  int V = Track->TrackElementAt(874, LeadElement).VLoc;
860  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
861  {
862  Crashed = true;
866  // no need for missed action logs - will be sent when train removed
867  }
868  }
869  }
871  {
873  }
874  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
876  //if Command == "" then either TimeLoc or TimeTimeLoc so don't hold, and added last part at v2.12.0 so don't hold if have both command == pas and Treat... flag
877  {
878  HoldAtLocationInTTMode = true;
879  }
880  else if(TrainMode == Timetable)
881  {
882  HoldAtLocationInTTMode = false;
883  }
884  // in Signaller mode HoldAtLocationInTTMode not changed
885 
886  // check if departure pending & set times unless already set
887  if(TrainMode == Timetable)
888  {
890  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
891  {
892  if((ActionVectorEntryPtr->Command != "pas") && (ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) && (ActualArrivalTime > TDateTime(0)))
893  {
894  AnsiString ReasonArray[24] = {"a driver is awaited","a guard is awaited","of a medical emergency","of a technical problem","of a security issue",
895  "of a safety issue","of a disturbance","a train crew member has been taken ill","the driver has been taken ill","the guard has been taken ill",
896  "a report has been received concerning safety","a shoe has been lost under the train","of a reported theft",
897  "of an incident involving an animal","some luggage has been lost under the train","a minor repair is needed","a suspicious object has to be dealt with safely",
898  "a door is stuck open","additional stock has to be attached","a security alert","of a train fault","of an operating incident","safety checks are required",
899  "of a shortage of on train crew"};
900  //(ActionVectorEntryPtr->Command != "pas") added at v2.13.0 to rule out passes, though probably not needed
901  //(ActualArrivalTime > TDateTime(0)) added at v2.13.0 to ensure that it has been set and to dismiss trains that are present
902  //at start or have no departure time set.
903  NewDelay = 0; //section relating to random delays added at v2.13.0
904  TDateTime TimetableReleaseTime = TrainController->GetRepeatTime(0, ActionVectorEntryPtr->DepartureTime, RepeatNumber, IncrementalMinutes); //Timetable value
905  TDateTime DwellTime = TimetableReleaseTime - ActualArrivalTime; //Timetable value
906  if(DwellTime < TDateTime(30.0 / 86400))
907  {
908  DwellTime = TDateTime(30.0 / 86400);
909  }
910  int randval = random(10000);
911  if(randval != 0) //if randval == 0 or DelayMode == Nil then NewDelay will be 0 as set above
912  {
913  if(Utilities->DelayMode == Minor)
914  {
915  if(randval < Utilities->MinorDelayCutoff)
916  {
917  NewDelay = Utilities->MinorDelayFactor * log(Utilities->MinorDelayCutoff/randval);//minutes (confusingly log in C++Builder gives the natural logarithm)
918  }
919  }
920  else if(Utilities->DelayMode == Moderate)
921  {
922  if(randval < Utilities->ModerateDelayCutoff)
923  {
925  }
926  }
927  else if(Utilities->DelayMode == Major)
928  {
929  if(randval < Utilities->MajorDelayCutoff)
930  {
932  }
933  }
934  }
935 //NewDelay = 25; //test
936  if(double(TrainController->TTClockTime) <= (Utilities->LastDelayTTClockTime + 5.0/1440.0)) //if within 5 mins of last delay for any train
937  { //then don't delay. Added at v2.13.0
938  NewDelay = 0;
939  }
940  if(NewDelay < 1)
941  {
942  NewDelay = 0;
943  }
944  if(NewDelay < double(DwellTime) * 1440) //if less than scheduled dwell time then no additional delay
945  {
946  NewDelay = 0;
947  }
948  else
949  {
950  NewDelay -= double(DwellTime) * 1440;//reduce delay by dwell time
951  }
952  if(DelayedRandMins > 0)
953  {
954  DelayedRandMins -= double(DwellTime) * 1440;//reduce knock-on random delay by dwell time
955  }
956  if(DelayedRandMins < 0)
957  {
958  DelayedRandMins = 0;//can't be less than zero
959  }
961  {
962  NewDelay -= DelayedRandMins; //NewDelay is the additional delay over and above the existing knock-on delay (from earlier random delays)
963  //the formula above already includes knock-on effects
964  DelayedRandMins += NewDelay; //the new total delay, knock-on + additional
965 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //don't add here, add when depart, else this value can be > late mins
966  }
967  else
968  {
969  NewDelay = 0;
970 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //as above
971  }
972  ReleaseTime = LastActionTime + TDateTime(NewDelay / 1440); //earliest possible release time
973  if(NewDelay < 0.5) //less than the 30 secs min interval
974  {
975  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
976  }
977  if(ReleaseTime < TimetableReleaseTime)
978  {
979  ReleaseTime = TimetableReleaseTime; //back to correct time
980  NewDelay = 0;
981  DelayedRandMins = 0;
982  }
983  if(DelayedRandMins > double(ReleaseTime - TimetableReleaseTime) * 1440)
984  {
985  DelayedRandMins = double(ReleaseTime - TimetableReleaseTime) * 1440; //reduce this if time has been made up
986  }
987 
988  if(DelayedRandMins < NewDelay) //may be if reduced above, but if so need to reduce NewDelay also
989  {
991  }
992  //may be possible to simplify all the above but as it seems to work ok leave as is
993  if(int(NewDelay) > 0) //additional delay over and above knock-on effects from earlier random delays
994  {
996  if(int(NewDelay) == 1)
997  {
999  ActionVectorEntryPtr->LocationName + " by 1 minute");
1000  PerfLogForm->PerformanceLog(18, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: " + HeadCode + " delayed at " +
1001  ActionVectorEntryPtr->LocationName + " by 1 minute because of a minor technical issue");
1002  TrainController->StopTTClockMessage(140, HeadCode + " delayed at " +
1003  ActionVectorEntryPtr->LocationName + " by 1 minute because of a minor technical issue");
1004  }
1005  else
1006  {
1007  Display->WarningLog(11, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " delayed at " +
1008  ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) + " minutes");
1009  if(NewDelay >= 10) //give variable reasons for >= 10 mins
1010  {
1011  int randval2 = rand() % 24; //24 reasons
1012  AnsiString Reason = ReasonArray[randval2];
1014  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1015  " minutes because " + Reason);
1016  TrainController->StopTTClockMessage(141, HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1017  " minutes because " + Reason);
1018  }
1019  else
1020  {
1022  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1023  " minutes because of a minor problem");
1024  TrainController->StopTTClockMessage(142, HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1025  " minutes because of a minor problem");
1026  }
1027  }
1028  }
1029  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1030  ActualArrivalTime = TDateTime(0); //only run through this section once per arrival
1031  DepartureTimeSet = true;
1032  }
1033  else if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) //as was, for trains that don't have an errival time set
1034  {//if have skipped to a new service then DepartureTime will be set (in above segement when earlier train arrived)
1035  //but ArrivalTime won't be set as it is reset to 0 at end of above segement when earlier train arrived, so this segement
1036  //will run without any random delays which might cause additional complications from mixing modifications and best avoided.
1037  NewDelay = 0;
1038  DelayedRandMins = 0;
1040  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1041  {
1042  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1043  }
1044  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1045  DepartureTimeSet = true;
1046  }
1047  else if((ActionVectorEntryPtr->Command == "pas") && TreatPassAsTimeLocDeparture) //new segment at v2.12.0 to treat a pass as a departure
1048  {//for when skip to a new service at a pass location. As above this also avoids any random delays, and will avoid above segment because
1049  //departure time isn't set - it's an event time. Again random delays in this situation might cause additional complications
1050  //from mixing modifications so best avoided.
1051  NewDelay = 0;
1052  DelayedRandMins = 0;
1054  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1055  {
1056  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1057  }
1058  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1059  DepartureTimeSet = true;
1060  }
1061  }
1062  }
1063  if(TrainController->OpTimeToActUpdateCounter == 0)// && TrainController->OpActionPanelVisible) removed last condition so always calc TimeToExit
1064  {
1065  OpTimeToAct = CalcTimeToAct(0, TimeToExit, ExitPair); // called after ReleaseTime set
1066 // this->TimeToExit = TimeToExit; don't need these as values updated directly
1067 // this->ExitPair = ExitPair;
1068  // calculate every 1 sec (in real time, not timetable time) for all trains
1069  }
1070  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
1071  if(TrainMode == Timetable)
1072  {
1073  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
1074  {
1075  RemainHereLogNotSent = true;
1076  }
1078  {
1079  // ignore TimeLoc & TTLoc departures
1080  // Action logs given in functions
1082  LastActionTime + TDateTime(30.0 / 86400)))
1083  {
1084  if(ActionVectorEntryPtr->Command == "fsp")
1085  {
1086  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
1087  FrontTrainSplit(0);
1088  if(TrainFailurePending) // ok, stopped so PlotElements set
1089  {
1090  TrainHasFailed(0);
1091  }
1092  Utilities->CallLogPop(2041);
1093  return;
1094  }
1095  else if(ActionVectorEntryPtr->Command == "rsp")
1096  {
1097  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
1098  RearTrainSplit(0);
1099  if(TrainFailurePending) // ok, stopped so PlotElements set
1100  {
1101  TrainHasFailed(1);
1102  }
1103  Utilities->CallLogPop(2042);
1104  return;
1105  }
1106  else if(ActionVectorEntryPtr->Command == "Fjo")
1107  {
1108  FinishJoin(0);
1109  }
1110  else if(ActionVectorEntryPtr->Command == "jbo")
1111  {
1112  JoinedBy(0);
1113  }
1114  else if(ActionVectorEntryPtr->Command == "cdt")
1115  {
1116  ChangeTrainDirection(0, false);
1117  }
1118  else if(ActionVectorEntryPtr->Command == "dsc")
1119  {
1124  }
1125  else if(ActionVectorEntryPtr->Command == "Fns")
1126  {
1127  NewTrainService(0, false);
1128  }
1129  else if(ActionVectorEntryPtr->Command == "Frh")
1130  {
1131  RemainHere(0);
1132  }
1133  else if(ActionVectorEntryPtr->Command == "Fer")
1134  {
1135  TimetableFinished = true;
1136  }
1137  // other aspects of 'Fer' dealt with in TTrain::HasTrainGone()
1138  else if(ActionVectorEntryPtr->Command == "F-nshs")
1139  {
1141  }
1142  else if(ActionVectorEntryPtr->Command == "Frh-sh")
1143  {
1144  RepeatShuttleOrRemainHere(0, false);
1145  }
1146  else if(ActionVectorEntryPtr->Command == "Fns-sh")
1147  {
1149  }
1150 /*
1151  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
1152  shuttle headcode (no train creation)
1153  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1154  remain here
1155  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1156  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
1157 */
1158  }
1159  }
1160  else
1161  {
1163  {
1165  }
1166  }
1167  }
1168  if(TrainMode == Timetable)
1169  {
1170  if(StoppedAtBuffers)
1171  {
1172  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
1173  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
1174  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
1175  if(BufferLocation == "")
1176  {
1178  }
1179  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
1180  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
1181  {
1185  {
1187  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1188  // Drop missed actions so user can still use sig mode to get back on track
1190  }
1191  if(TrainFailurePending) // ok, stopped so PlotElements set
1192  {
1194  TrainHasFailed(2);
1195  }
1196  Utilities->CallLogPop(1020);
1197  return;
1198  }
1199  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !RevisedStoppedAtLoc() && (TrainController->TTClockTime >
1200  ReleaseTime))
1201  {
1204  {
1207  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1208  // Drop missed actions so user can still use sig mode to get back on track
1210  }
1211  if(TrainFailurePending) // ok, stopped so PlotElements set
1212  {
1214  TrainHasFailed(3);
1215  }
1216  Utilities->CallLogPop(1397);
1217  return;
1218  }
1219  }
1220  else
1221  {
1223  }
1224  }
1225  else
1226  {
1228  }
1229  if(TrainMode == Timetable)
1230  {
1232  {
1234  }
1236  {
1238  }
1239  }
1240  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1241  // restart after stopped for train in front
1242  int NextElementPosition, NextEntryPos;
1243 
1244  if(LeadElement > -1) // if an exit continuation then not set
1245  {
1246  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1247  {
1249  }
1250  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1251  {
1252  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1253  {
1254  LeadExitPos = 1;
1255  }
1256  else
1257  {
1258  LeadExitPos = 3;
1259  }
1260  }
1261  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1262  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1263  }
1264  else
1265  {
1266  NextElementPosition = -1;
1267  NextEntryPos = -1;
1268  }
1269  if((NextElementPosition > -1) && (NextEntryPos > -1))
1270  // may be buffers or continuation so need this check
1271  {
1272 /*
1273  Check whether calling-on conditions met:-
1274  a) approaching train has stopped at a signal but not at a location;
1275  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1276  change of direction (cdt), remaining here (Frh), or under signaller control);
1277  c) at least 1 platform available for the approaching train;
1278  d) points (if any) set for direct route into platform;
1279  e) approaching train is to stop at station;
1280  f) no more facing signals between train and platform;
1281  g) [dropped g]
1282  h) train in front preventing route being set far enough to release stop signal;
1283  i) train in front not exiting at continuation;
1284  j) signal must be within 4km of the stop platform;
1285  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily];
1286  l) no existing route conflicts with the route into the platform; and
1287  m) not failed or without power (these added at v2.10.0)
1288  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1289  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1290 */
1291  if(TrainMode == Timetable)
1292  {
1293  if(CallingOnAllowed(0)) //returns false if failed or no power (modified afer v2.9.2)
1294  {
1295  CallingOnFlag = true;
1296  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1297  }
1298  else
1299  {
1300  if(CallingOnFlag) //TrainHasFailed sets this flag to false (at v2.10.0)
1301  {
1302  if(!TrainFailed) //shouldn't be needed but include for safety at v2.10.0
1303  {
1305  }
1306  }
1307  CallingOnFlag = false;
1308  }
1309  }
1310  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !RevisedStoppedAtLoc())
1311  {
1312  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1313  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1314  // sets StoppedAtSignal again & train doesn't move
1315  StoppedAtSignal = false;
1316  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1317  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1318  // LeadMidLag and front of train was on LeadElement (after the current move)
1320  EntrySpeed = 0;
1322  FirstHalfMove = true;
1323  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1324  // NextElement is the element to be entered
1325  }
1327  {
1328  if(ClearToNextSignal(0))
1329  {
1330  StoppedForTrainInFront = false;
1331  BeingCalledOn = false;
1332  EntrySpeed = 0;
1334  FirstHalfMove = true;
1335  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1336  }
1337  else
1338  {
1339  if(TrainFailurePending) // ok, stopped so PlotElements set
1340  {
1341  TrainHasFailed(4);
1342  }
1343  Utilities->CallLogPop(1097);
1344  return;
1345  }
1346  }
1347  }
1348  if((Straddle == MidLag) && (LeadElement != -1))
1349  // later check only for Straddle == LeadMid, so need this check here for initial train start
1350  {
1352  }
1353 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1354  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1355  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1356  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1357  which could be when start as Snt.
1358  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1359  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1360  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1361  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1362  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1363  reached.
1364  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1365  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1366  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1367  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1368  sending a message to the performancelog.
1369 */
1370 
1371  if(TrainMode == Timetable)
1372  {
1374  {
1375  if(BeingCalledOn)
1376  {
1377  StoppedForTrainInFront = true;
1378  }
1380  {
1382  }
1383  else //added at v2.14.0 as if a train ready to depart (pink b'gnd) taken under sig control then restored to tt control b'gnd stayed pink,
1384  { //even though release time now 30 seconds after tt control restored
1386  }
1388  {
1389  // value updated at every scheduled departure & arrival
1391  AnsiString StationName;
1393  {
1395  }
1397  {
1399  }
1400  else
1401  {
1402  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1403  }
1404  EntrySpeed = 0;
1408  FirstHalfMove = true;
1409  StoppedAtLocation = false;
1410 
1411  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1412  {
1413  StoppedWithoutPower = true;
1414  }
1415  if((NextElementPosition > -1) && (NextEntryPos > -1))
1416  // condition check added for SloughIECC error reported by James U
1417  {
1418  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1419  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1420  {
1421  StoppedAtSignal = true;
1422  if(!StoppedWithoutPower)
1423  // if stopped without power just keep existing background colour
1424  {
1426  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1427  }
1428  }
1429  }
1431  {
1432  TimeTimeLocArrived = false;
1433  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1434  // no warning for TimeTimeLoc departure
1435  }
1436  else if(TreatPassAsTimeLocDeparture) //added at v2.12.0 so late/early/on time mins recorded accurately
1437  {
1438  LogAction(36, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->EventTime, ActionVectorEntryPtr->Warning); //EventTime because the real event is a pass
1439  }
1440  else //must be TimeLoc departure
1441  {
1443  }
1444  TreatPassAsTimeLocDeparture = false; //added at v2.12.0, reset after train departs
1445  DepartureTimeSet = false;
1446  // no need to set LastActionTime for a departure
1447  //deal here with departure pointer change, increment if SkippedDeparture
1448  CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //only add these after late mins added (in LogAction)
1449 
1450  if(SkippedDeparture)
1451  {
1454  TrainSkippedEvents = 0;
1455  SkippedDeparture = false;
1456  SkipPtrValue = 0;
1457  ActionsSkippedFlag = false;
1458  }
1459  else
1460  {
1462  }
1463  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1464  // note
1465 /*
1466  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1467  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1468  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1469  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1470  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1471  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1472  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1473 */
1475  {
1476  StoppedAtBuffers = true;
1477  }
1478  else if(!StoppedWithoutPower)
1479  // if buffers or no power, don't set values
1480  {
1482  {
1483  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1484  // NextElement is the element to be entered
1485  }
1486  else
1487  {
1489  // use LeadElement for an exit continuation
1490  }
1491  }
1492  }
1493  }
1494  }
1495  if(Straddle == LeadMidLag) //train on a half element
1496  {
1498  {
1499  Utilities->CallLogPop(654);
1500  return;
1501  }
1502  }
1503  else //train fully on 2 elements
1504  {
1506  {
1507  Utilities->CallLogPop(655);
1508  return;
1509  }
1510  }
1511  if((LeadElement > -1) && (MidElement > -1))
1512  {
1514  {
1515  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1516  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1517  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1518  SignallerStoppingFlag = false;
1519  StepForwardFlag = false;
1520  }
1521  }
1522  if(Stopped())
1523  // this is what prevents another movement if the train is stopped
1524  {
1525  if(TrainFailurePending) // ok, stopped so PlotElements set
1526  {
1527  TrainHasFailed(5);
1528  }
1529  BrakeRate = 0;
1530  Utilities->CallLogPop(656);
1531  return;
1532  }
1533 
1534  // HERE WHEN READY FOR NEXT MOVE
1535 
1536  //added at v2.10.0 to set SPADFlag if red signal immediately ahead (as it will be if in a locked route)
1537  //check if due to run past a red signal & if so set SPADFlag (SetTrainMovementValues & its SPAD check only called when arrive fully on 2 elements)
1538  if(Straddle == LeadMid) //fully on 2 elements
1539  {
1540  if(LeadElement > -1)
1541  {
1542  if(Track->TrackElementAt(1402, LeadElement).Conn[LeadExitPos] > -1)
1543  {
1545  if(TIF.TrackType == SignalPost)
1546  {
1547  int TIFEntryPos = Track->TrackElementAt(1405, LeadElement).ConnLinkPos[LeadExitPos];
1548  int TIFExitPos = 0;
1549  if(TIFEntryPos == 0)
1550  {
1551  TIFExitPos = 1;
1552  }
1553  if((TIF.Config[TIFExitPos] == Signal) && TIF.Attribute == 0 && (ExitSpeedHalf > 1) && !AllowedToPassRedSignal && !TIF.CallingOnSet) //use ExitSpeedHalf as may have been stopped at signal so entryspeed is 0
1555  {
1556  SPADFlag = true; // user has to intervene to reset & restart after spad
1557  }
1558  }
1559  }
1560  }
1561  }
1562 
1563  // check for train in front & if so stop at next access (when train fully on element next to train)
1564  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1565  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1566  // variable TrainInFrontInSignallerModeFlag
1567  {
1568  if(LeadElement > -1)
1569  {
1570  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1571  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1572  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1573  // true if another train on NextEntryPos track whether bridge or not
1574  {
1575  StoppedForTrainInFront = true;
1576  }
1577  else
1578  {
1579  StoppedForTrainInFront = false;
1580  }
1581  }
1582  }
1583  if((Straddle == LeadMid) && SPADFlag)
1584  // give message + plot background when ready to move half past the signal
1585  {
1586  if(NextElementPosition > -1)
1587  {
1588  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1589  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1590  {
1591  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1593  // if goes past 2 signals then give message twice
1595  }
1596  }
1597  }
1598  if(Straddle == LeadMidLag)
1599  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1600  {
1601  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1602  if(SPADFlag)
1603  {
1604  if(ExitSpeedFull == 0)
1605  {
1606  StoppedAfterSPAD = true;
1607  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1608  }
1609  }
1611  {
1612  if(ExitSpeedFull == 0)
1613  {
1614  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1615  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1616  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1617  // is sent at the right time and once only.
1618  SignallerStopped = true;
1619  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1620  StepForwardFlag = false;
1621  SignallerStoppingFlag = false;
1622  TTrackElement TE;
1623  AnsiString Loc = "";
1624  bool LocNamed = false;
1625  if(LeadElement > -1)
1626  {
1627  TE = Track->TrackElementAt(782, LeadElement);
1628  if(TE.ActiveTrackElementName != "")
1629  {
1630  Loc = TE.ActiveTrackElementName;
1631  LocNamed = true;
1632  }
1633  else
1634  {
1635  Loc = "track element " + TE.ElementID;
1636  }
1637  }
1638  if((MidElement > -1) && !LocNamed)
1639  {
1640  TE = Track->TrackElementAt(783, MidElement);
1641  if(TE.ActiveTrackElementName != "")
1642  {
1643  Loc = TE.ActiveTrackElementName;
1644  LocNamed = true;
1645  }
1646  else if(Loc == "")
1647  {
1648  Loc = "track element " + TE.ElementID;
1649  }
1650  }
1651  if(Loc == "")
1652  {
1653  Loc = "outside railway";
1654  // must have stopped after left at a continuation (because both lead & mid == -1)
1655  }
1656  else
1657  {
1658  Loc = "at " + Loc;
1659  }
1660  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1661  }
1662  }
1663  if(LeadElement > -1) // if an exit continuation then not set
1664  {
1665  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1666  {
1668  }
1669  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1670  {
1671  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1672  {
1673  LeadExitPos = 1;
1674  }
1675  else
1676  {
1677  LeadExitPos = 3;
1678  }
1679  }
1680  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1681  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1682  }
1683  else
1684  {
1685  NextElementPosition = -1;
1686  NextEntryPos = -1;
1687  }
1690  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1691 
1692  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1693  {
1694  StoppedWithoutPower = true;
1695  }
1696  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1697  // may be buffers or continuation. SPADFlag added at v2.1.0
1698  // so don't override the SPAD colour & don't set StoppedAtSignal
1699  {
1700  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1701  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !RevisedStoppedAtLoc())
1702  {
1703  StoppedAtSignal = true;
1704  if(!StoppedWithoutPower)
1705  // leave background as is if no power, but set StoppedAtSignal
1706  {
1708  }
1709  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1710  }
1711  }
1712  if(!Stopped())
1713  {
1714  if((NextElementPosition > -1) && (NextEntryPos > -1))
1715  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1716  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1717  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1718  // function for fuller explanation
1719  {
1720  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1721  // NextElement is the element to be entered
1722  }
1723  // follow the continuation exits:-
1724  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1725  {
1727  // Use LeadElement for calcs if lead is a continuation
1728  }
1729  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1730  {
1732  // Use MidElement for calcs if mid is a continuation
1733  }
1734  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1735  {
1737  // Use LagElement for calcs if lag is a continuation
1738  }
1739  }
1740  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1741  if((AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute))
1742  // Trains may not be in a route
1743  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1744  {
1745  // NB if LeadElement == -1 then the above test returns NoRoute
1746  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1747  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1748  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1749  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1750  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1751  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1752  FirstPair.second).GetELink() == TempELink))
1753  {
1754  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1755  }
1756  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1757  SecondPair.second).GetELink() == TempELink))
1758  {
1759  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1760  }
1761  }
1762  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1763  // Trains may not be in a route
1764  {
1765  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1766  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1767  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1768  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1769  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1770  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1771  FirstPair.second).GetELink() == TempELink))
1772  {
1773  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1774  }
1775  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1776  SecondPair.second).GetELink() == TempELink))
1777  {
1778  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1779  }
1780  }
1781  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1782  // Trains may not be in a route
1783  {
1784  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1785  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1786  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1787  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1788  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1789  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1790  FirstPair.second).GetELink() == TempELink))
1791  {
1792  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1793  }
1794  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1795  SecondPair.second).GetELink() == TempELink))
1796  {
1797  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1798  }
1799  AllRoutes->CheckMapAndRoutes(8); // test
1800  }
1801  if(LagElement > -1)
1802  // not entering at a continuation so can deal with train leaving the lag element
1803  {
1805  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1806  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1807  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1808 
1809  TPrefDirElement PrefDirElement;
1810  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1811  // as this is a 16x16 graphic
1813  {
1815  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1816  }
1817  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1818  {
1819  int RouteNumber;
1820  TrainGone = true;
1821  // flag to indicate train to be deleted - outside this function
1823  {
1824  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1825  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1826  // calc distance from & inc last signal to exit
1827  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1828  int NewLastElement = 0, NewLastExitPos = 0;
1829  // need above because can't change LastElement & LastExitPos until both new values obtained
1830  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1831  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1832  LastElement).TrackType != Points))
1833  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1834  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1835  // leave CumDistance as it was in these circumstances.
1836  {
1837  if(LastExitPos < 2)
1838  {
1839  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1840  }
1841  else
1842  {
1843  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1844  }
1845  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1846  if(NewLastElement == -1)
1847  // this will catch buffers or any other connection failure
1848  {
1849  break; //throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain"); //dropped at v2.15.0 because of Brent Mackie's error file of
1850  } //10/02/23, had two continuations linked with no signal between
1851  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)]; //so when train exited this routine tracked
1852  if(NewLastExitPos == -1) //back to the entry continuation which had no further connection - doesn't need to be an error at all!
1853  {
1854  break; //throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain"); //dropped at v2.15.0 because of Brent Mackie's error file of 10/02/23 , see above
1855  }
1856  LastElement = NewLastElement;
1857  LastExitPos = NewLastExitPos;
1858  }
1859  // if at signal add this in too (may not be signal if 'break;' encountered but doesn't matter)
1860  if(CumDistance < 1200)
1861  {
1862  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1863  }
1864  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1865  // else use 1200m - CumDistance
1866  int FirstDistance = 0;
1867  if(CumDistance >= 1200)
1868  {
1869  FirstDistance = 100;
1870  }
1871  else
1872  {
1873  FirstDistance = 1200 - CumDistance;
1874  }
1875  if(FirstDistance < 100)
1876  {
1877  FirstDistance = 100; // don't allow < 100
1878  }
1879  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1880  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1881  if(ExitSpeedFull > 20.0)
1882  {
1883  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1884  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1885  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1886  // 4320.0 = 3.6 * 1200, .0 to make it a double
1887  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1888  }
1889  else
1890  {
1891  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1892  ContinuationAutoSigEntry.SecondDelay = 120.0;
1893  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1894  }
1895  ContinuationAutoSigEntry.AccessNumber = 0;
1896  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1898  {
1900  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1901  VectorIT++)
1902  {
1903  if(VectorIT->RouteNumber == RouteNumber)
1904  {
1905  // another train has passed out of same route so erase earlier entry
1906  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1907  break;
1908  }
1909  }
1910  }
1911  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1912  }
1914  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1915  Display->Update();
1916  // need to keep this since Update() not called for PlotSmallOutput as too slow
1917  Utilities->CallLogPop(659);
1918  return;
1919  }
1920  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1921  if(LeadElement > -1)
1922  {
1923  TTrackElement &TE = Track->TrackElementAt(224, LeadElement); //added at v2.13.0 for brevity
1924  if(TE.Config[LeadExitPos] == Signal)
1925  // changed to lead so reset early
1926  {
1927  LastSigPassedFailed = false; //used to cancel route elements up to next signal for autosigs route
1928  TE.Attribute = 0; // red
1929  int RouteNumber; //only used for autosigs routes
1930  //add chance to fail when train passes a signal
1931  if((random(Utilities->SignalChangeEventsPerFailure) == 0) && !TE.Failed && (Utilities->FailureMode != FNil) &&
1932  (TrainMode == Timetable) && !TE.CallingOnSet) //can't fail twice, calling on signal can't fail
1933  {
1935  IFE.TVPos = LeadElement;
1936  TE.Failed = true;
1937  Display->WarningLog(19, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Signal failed at " + TE.ElementID);
1938  PerfLogForm->PerformanceLog(42, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: Signal failed at " + TE.ElementID);
1939  TrainController->StopTTClockMessage(129, "Signal at " + TE.ElementID +
1940  " failed when changing aspect.\nTrains can only pass under signaller control.");
1941  AllRoutes->RebuildRailwayFlag = true; //force ClearandRebuildRailway at next clock tick
1942  LastSigPassedFailed = true;
1943  //set repair time, random value in minutes between 10 and 179
1944  double FailureMinutes = double(random(Utilities->MaxRandomRepairTime) + Utilities->FixedMinRepairTime); //between 10 and 179 minutes at random
1945  TDateTime RepairTime = TrainController->TTClockTime + TDateTime(FailureMinutes / 1440);
1946  IFE.RepairTime = RepairTime;
1948  Track->FailedSignalsVector.push_back(IFE); //rearwards signals will be set when LagElement leaves signal
1949  }
1950  TE.CallingOnSet = false;
1951  // don't plot if zoomed out
1952  if(!Display->ZoomOutFlag)
1953  {
1955  }
1956  // covers signal resetting in same direction
1957  }
1958  }
1960  {
1961  AllRoutes->RebuildRailwayFlag = true; //added at v2.13.0 to replot signal after train left in case it had failed
1962  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1963  {
1964  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1965  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1966  TPrefDirElement PrefDirElement;
1967  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1969  {
1971  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1972  }
1974  LockedVectorNumber)))
1975  {
1977  }
1978  }
1979  }
1980  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1981  {
1982  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1984  // don't plot if zoomed out
1985  if(!Display->ZoomOutFlag)
1986  {
1988  }
1989  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1990  }
1992  {
1993  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1994  {
1995  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1996  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1997  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1998  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1999  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
2000  int RouteNumber;
2002  // already know it's an autosigsroute, this is just to get the RouteNumber
2003  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
2004  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
2005  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
2006  int RouteNumber2;
2008  // already know it's an autosigsroute, this is just to get the RouteNumber
2009  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
2010  // note that if not in a route (as likely) then RouteNumber2 set to -1
2011  {
2012  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
2013  // this was in the 1.3.0 addition but without the condition
2014  }
2015  // end of 1.3.2 addition
2016  // end of 1.3.0.addition
2017  }
2018  TPrefDirElement PrefDirElement;
2019  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
2021  {
2023  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
2024  }
2025  }
2026  }
2027  }
2028  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
2029  if(Straddle == LeadMid)
2030  {
2031  AllowedToPassRedSignal = false;
2032  // if had been allowed to pass then at this point it will move half onto signal so can be reset
2033  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
2034  if(DerailPending)
2035  // set during last GetLeadElement, but only act on it when train fully on offending point
2036  // i.e. next time Straddle reaches LeadMid
2037  {
2038  Derailed = true;
2039  DerailPending = false;
2043  Utilities->CallLogPop(657);
2044  return;
2045  }
2052  Straddle = MidLag;
2053  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
2054  // LeadElement during this function (note that if stopped at signal then won't get this far)
2055  if(LeadElement > -1)
2056  {
2058  // i.e an exit continuation only
2059  // if don't exclude entry continuations then can't progress past it
2060  {
2061  LeadElement = -1;
2062  }
2063  else
2064  {
2065  GetLeadElement(0);
2066  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
2068  if(Stopped())
2069  {
2070  if(TrainFailurePending) // ok, stopped so PlotElements set
2071  {
2072  TrainHasFailed(6);
2073  }
2074  Utilities->CallLogPop(658);
2075  return; // i.e. don't move forward one step if next element is a red signal
2076  }
2077  }
2078  }
2079  }
2080  if(LagElement > -1)
2081  {
2082  // below are the actions required at both half moves for LagElement > -1
2084 
2085  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
2086  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
2087  // need to do this for each half element
2088 
2089  TPrefDirElement PrefDirElement;
2090  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
2091  {
2092  int RouteNumber; // holder for call below - not used
2094  {
2095  if(Utilities->clTransparent == TColor(0xFFFFFF))
2096  // change to black for a white background
2097  {
2099  // only applies for AutoSigs Route in case was locked & timed out
2100  }
2101  else
2102  // change to white for a dark background
2103  {
2105  // only applies for AutoSigs Route in case was locked & timed out
2106  }
2108  }
2109  }
2111  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
2112  // or a train on the opposite track - needs to be replotted
2113  }
2114  // update all array values
2115  HOffset[3] = HOffset[2];
2116  HOffset[2] = HOffset[1];
2117  HOffset[1] = HOffset[0];
2118  VOffset[3] = VOffset[2];
2119  VOffset[2] = VOffset[1];
2120  VOffset[1] = VOffset[0];
2121  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
2122 
2123  BackgroundPtr[3] = BackgroundPtr[2];
2124  BackgroundPtr[2] = BackgroundPtr[1];
2125  BackgroundPtr[1] = BackgroundPtr[0];
2126  BackgroundPtr[0] = TempPtr;
2127 
2128  // update headcode graphics depending on Lead entry value
2129  if(LeadElement > -1) // if Lead is -1 then stays as is
2130  {
2132  {
2133  for(int x = 0; x < 4; x++)
2134  {
2135  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
2136  }
2137  }
2138  else
2139  {
2140  for(int x = 0; x < 4; x++)
2141  {
2143  }
2144  }
2145  }
2146  if(TrainMode == Timetable)
2147  {
2149  }
2150  else
2151  {
2153  }
2155 
2156  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
2157  if(LeadElement > -1)
2158  {
2159  if(Straddle == MidLag)
2160  // just about to move half onto the new lead element
2161  {
2163  // pick up new background bitmap [0]
2165  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
2166  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
2167  // check if own ID for entry at continuation, else crashes into itself!
2168  {
2169  // OK if crossing on a bridge
2170  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
2171  if(OtherTrainEntryPos == -1)
2172  {
2173  throw Exception("Error - OtherTrainEntryPos not set");
2174  }
2175  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
2176  // LeadEntryPos for rear end crashes
2177  (LeadExitPos == OtherTrainEntryPos))
2178  // LeadExitPos for head-on crashes
2179  {
2181  Crashed = true; // only set if Straddle = MidLag
2182  CallingOnFlag = false;
2183  // in case was set, need to disable call on if call on button had been pressed
2184  }
2185  }
2186  else if(MidElement > -1) // will be -1 for continuation entries
2187  {
2188  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
2189  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
2190  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
2191  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
2192  int OtherTrainID = -1;
2193  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
2194  {
2195  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
2196  {
2197  TrainCrashedInto = OtherTrainID;
2198  Crashed = true; // only set if Straddle = MidLag
2199  CallingOnFlag = false;
2200  // in case was set, need to disable call on if call on button had been pressed
2201  }
2202  }
2203  }
2204  }
2205  else
2206  {
2208  // pick up new background bitmap [0]
2210  }
2211  PlotElement[0] = LeadElement;
2213  PlotTrainGraphic(12, 0, Display);
2214  }
2215  if(MidElement > -1)
2216  {
2217  PlotElement[2] = MidElement;
2219  PlotTrainGraphic(1, 2, Display);
2220  }
2221  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
2222  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
2223  if(Straddle == MidLag)
2224  {
2225  if(MidElement > -1)
2226  {
2227  PlotElement[1] = MidElement;
2229  PlotTrainGraphic(2, 1, Display);
2230  }
2231  if(LagElement > -1)
2232  {
2233  PlotElement[3] = LagElement;
2235  PlotTrainGraphic(3, 3, Display);
2236  }
2237  }
2238  else // Straddle == LeadMidLag
2239  {
2240  if(LeadElement > -1)
2241  {
2242  PlotElement[1] = LeadElement;
2244  PlotTrainGraphic(4, 1, Display);
2245  }
2246  if(MidElement > -1)
2247  {
2248  PlotElement[3] = MidElement;
2250  PlotTrainGraphic(5, 3, Display);
2251  }
2252  }
2253  if(Crashed)
2254  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
2255  {
2260  // in case was set, need to disable call on if call on button had been pressed
2267  Straddle = LeadMidLag;
2268  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
2269  Display->Update();
2270  // resurrected when Update() dropped from PlotOutput etc
2271  Utilities->CallLogPop(660);
2272  return;
2273  }
2274  // deal here with station stops & pass times after all replotting done but before Straddle changed
2275  if(TrainMode == Timetable)
2276  {
2277  if(Straddle == LeadMidLag)
2278  {
2279  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2280  {
2281  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2282  // to point to the location arrival entry - before a change of direction
2283  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2284  bool StopRequired = false;
2285  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2286  if(TTVPos > -1) // -1 if can't find it or if name is ""
2287  {
2288  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2289  // or a station where next element contains a train or a stop signal, if so
2290  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2291  // to test the actual track the train is on since it can't be a platform
2292  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2293  TTrackElement NextTrackElement; // default for now
2294  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2295  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2296  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2297  int NextElementEntryPos = -1;
2298  int NextElementExitPos = -1;
2299  bool TrainOnNextElement = false;
2300  bool StopSignalAtNextElement = false;
2301  if(ForwardConnection)
2302  // if no forward connection can't derive anything from it without errors
2303  {
2304  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2305  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2306  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2307  // this is only for signals so no need to worry about points ambiguity
2308  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2309  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2310  }
2311  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2312  {
2313  if(TTVPos > 0)
2314  {
2316  ActionVectorEntryPtr += TTVPos;
2317  }
2318  if(StopRequired)
2319  {
2320  StoppedAtLocation = true;
2321  StoppedAtSignal = false;
2322  // may have been set earlier at line 925 so need to reset as
2323  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2324  // in zoom out mode
2325  if(!TrainFailed)
2326  {
2328  // pale green
2329  }
2331  ActualArrivalTime = TrainController->TTClockTime; //added at v2.13.0
2333  {
2334  TimeTimeLocArrived = true;
2335  // used in case of later signaller control, when need to know
2336  // whether had arrived or not, to avoid sending the arrival
2337  // message twice, see TInterface::TimetableControl1Click
2338  }
2339  }
2340  else
2341  {
2343  }
2345  {
2347  }
2348  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2350  }
2351  }
2352  }
2353  }
2354  }
2355  if(Straddle == MidLag)
2356  {
2357  Straddle = LeadMidLag;
2358  FirstHalfMove = false;
2359  }
2360  else if(Straddle == LeadMidLag)
2361  {
2362  Straddle = LeadMid;
2363  FirstHalfMove = true;
2364  }
2365  else if(Straddle == LeadMid)
2366  {
2367  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2368  }
2369  if(TrainFailurePending) // ok, moving but PlotElements set above
2370  {
2371  TrainHasFailed(7);
2372  }
2373  Display->Update();
2374  // need to keep this since Update() not called for PlotSmallOutput as too slow
2375  Utilities->CallLogPop(661);
2376 }
2377 
2378 // ----------------------------------------------------------------------------
2379 
2380 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2381 {
2382  switch(CodeChar)
2383  {
2384  case '0':
2385  return(RailGraphics->Code0);
2386 
2387  case '1':
2388  return(RailGraphics->Code1);
2389 
2390  case '2':
2391  return(RailGraphics->Code2);
2392 
2393  case '3':
2394  return(RailGraphics->Code3);
2395 
2396  case '4':
2397  return(RailGraphics->Code4);
2398 
2399  case '5':
2400  return(RailGraphics->Code5);
2401 
2402  case '6':
2403  return(RailGraphics->Code6);
2404 
2405  case '7':
2406  return(RailGraphics->Code7);
2407 
2408  case '8':
2409  return(RailGraphics->Code8);
2410 
2411  case '9':
2412  return(RailGraphics->Code9);
2413 
2414  case 'A':
2415  return(RailGraphics->CodeA);
2416 
2417  case 'B':
2418  return(RailGraphics->CodeB);
2419 
2420  case 'C':
2421  return(RailGraphics->CodeC);
2422 
2423  case 'D':
2424  return(RailGraphics->CodeD);
2425 
2426  case 'E':
2427  return(RailGraphics->CodeE);
2428 
2429  case 'F':
2430  return(RailGraphics->CodeF);
2431 
2432  case 'G':
2433  return(RailGraphics->CodeG);
2434 
2435  case 'H':
2436  return(RailGraphics->CodeH);
2437 
2438  case 'I':
2439  return(RailGraphics->CodeI);
2440 
2441  case 'J':
2442  return(RailGraphics->CodeJ);
2443 
2444  case 'K':
2445  return(RailGraphics->CodeK);
2446 
2447  case 'L':
2448  return(RailGraphics->CodeL);
2449 
2450  case 'M':
2451  return(RailGraphics->CodeM);
2452 
2453  case 'N':
2454  return(RailGraphics->CodeN);
2455 
2456  case 'O':
2457  return(RailGraphics->CodeO);
2458 
2459  case 'P':
2460  return(RailGraphics->CodeP);
2461 
2462  case 'Q':
2463  return(RailGraphics->CodeQ);
2464 
2465  case 'R':
2466  return(RailGraphics->CodeR);
2467 
2468  case 'S':
2469  return(RailGraphics->CodeS);
2470 
2471  case 'T':
2472  return(RailGraphics->CodeT);
2473 
2474  case 'U':
2475  return(RailGraphics->CodeU);
2476 
2477  case 'V':
2478  return(RailGraphics->CodeV);
2479 
2480  case 'W':
2481  return(RailGraphics->CodeW);
2482 
2483  case 'X':
2484  return(RailGraphics->CodeX);
2485 
2486  case 'Y':
2487  return(RailGraphics->CodeY);
2488 
2489  case 'Z':
2490  return(RailGraphics->CodeZ);
2491 
2492  case 'a':
2493  return(RailGraphics->Code_a);
2494 
2495  case 'b':
2496  return(RailGraphics->Code_b);
2497 
2498  case 'c':
2499  return(RailGraphics->Code_c);
2500 
2501  case 'd':
2502  return(RailGraphics->Code_d);
2503 
2504  case 'e':
2505  return(RailGraphics->Code_e);
2506 
2507  case 'f':
2508  return(RailGraphics->Code_f);
2509 
2510  case 'g':
2511  return(RailGraphics->Code_g);
2512 
2513  case 'h':
2514  return(RailGraphics->Code_h);
2515 
2516  case 'i':
2517  return(RailGraphics->Code_i);
2518 
2519  case 'j':
2520  return(RailGraphics->Code_j);
2521 
2522  case 'k':
2523  return(RailGraphics->Code_k);
2524 
2525  case 'l':
2526  return(RailGraphics->Code_l);
2527 
2528  case 'm':
2529  return(RailGraphics->Code_m);
2530 
2531  case 'n':
2532  return(RailGraphics->Code_n);
2533 
2534  case 'o':
2535  return(RailGraphics->Code_o);
2536 
2537  case 'p':
2538  return(RailGraphics->Code_p);
2539 
2540  case 'q':
2541  return(RailGraphics->Code_q);
2542 
2543  case 'r':
2544  return(RailGraphics->Code_r);
2545 
2546  case 's':
2547  return(RailGraphics->Code_s);
2548 
2549  case 't':
2550  return(RailGraphics->Code_t);
2551 
2552  case 'u':
2553  return(RailGraphics->Code_u);
2554 
2555  case 'v':
2556  return(RailGraphics->Code_v);
2557 
2558  case 'w':
2559  return(RailGraphics->Code_w);
2560 
2561  case 'x':
2562  return(RailGraphics->Code_x);
2563 
2564  case 'y':
2565  return(RailGraphics->Code_y);
2566 
2567  case 'z':
2568  return(RailGraphics->Code_z);
2569 
2570  default:
2571  return(RailGraphics->TempHeadCode);
2572  }
2573 }
2574 
2575 // ----------------------------------------------------------------------------
2576 
2577 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2578 {
2579  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2580  if(Code.Length() != 4)
2581  {
2582  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2583  }
2584  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2585  {
2586  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2587  }
2588  if(BackgroundColour != clB5G5R5)
2589  // i.e. not the basic graphic colour as loaded from resource file
2590  {
2591  for(int x = 0; x < 4; x++)
2592  {
2594  }
2595  }
2596  Utilities->CallLogPop(1484);
2597 }
2598 
2599 // ----------------------------------------------------------------------------
2600 
2601 void TTrain::GetLeadElement(int Caller)
2602 // assumes Mid & Lag already set, sets LeadElement,
2603 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2604 {
2605  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2606  DerailPending = false;
2610  {
2611  // attr 0=straight, - links 0 & 1 (0 = lead)
2612  // attr 1=diverging, - links 2 & 3 (2 = lead)
2613  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2614  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2615 
2616  // if enter at lead, exit at whatever attr set at
2617  // if enter at lag, exit at lead, but set derail wrt attribute
2618  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2619  {
2620  LeadExitPos = 1;
2621  }
2622 
2623  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2624  // best to be on safe side
2625  else if(LeadEntryPos == 0)
2626  {
2627  LeadEntryPos = 2;
2628  LeadExitPos = 3;
2629  }
2630  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2631  {
2632  LeadEntryPos = 0;
2633  LeadExitPos = 1;
2634  }
2635  else if(LeadEntryPos == 2)
2636  {
2637  LeadExitPos = 3;
2638  }
2639  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2640  {
2641  LeadExitPos = 0;
2642  }
2643  else if(LeadEntryPos == 1)
2644  {
2645  LeadExitPos = 0;
2646  DerailPending = true;
2647  }
2648  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2649  {
2650  LeadExitPos = 0;
2651  DerailPending = true;
2652  }
2653  else if(LeadEntryPos == 3)
2654  {
2655  LeadExitPos = 0;
2656  }
2657  }
2658  else if(LeadEntryPos == 0)
2659  {
2660  LeadExitPos = 1;
2661  }
2662  else if(LeadEntryPos == 1)
2663  {
2664  LeadExitPos = 0;
2665  }
2666  else if(LeadEntryPos == 2)
2667  {
2668  LeadExitPos = 3;
2669  }
2670  else if(LeadEntryPos == 3)
2671  {
2672  LeadExitPos = 2;
2673  }
2674  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2675 /* signal check moved to Update() function
2676  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2677  && (TrackElement.Attribute == 0))//0 = red
2678  {
2679  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2680  }
2681  else
2682  {
2683  StoppedAtSignal = false;
2684  }
2685 */
2686  Utilities->CallLogPop(662);
2687 }
2688 
2689 // ----------------------------------------------------------------------------
2690 
2691 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2692 {
2693  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2694  switch(Link)
2695  {
2696  case 1:
2697  {
2698  HOffset = 0;
2699  VOffset = 0;
2700  break;
2701  }
2702 
2703  case 2:
2704  {
2705  HOffset = 4;
2706  VOffset = 0;
2707  break;
2708  }
2709 
2710  case 3:
2711  {
2712  HOffset = 8;
2713  VOffset = 0;
2714  break;
2715  }
2716 
2717  case 4:
2718  {
2719  HOffset = 0;
2720  VOffset = 4;
2721  break;
2722  }
2723 
2724  case 6:
2725  {
2726  HOffset = 8;
2727  VOffset = 4;
2728  break;
2729  }
2730 
2731  case 7:
2732  {
2733  HOffset = 0;
2734  VOffset = 8;
2735  break;
2736  }
2737 
2738  case 8:
2739  {
2740  HOffset = 4;
2741  VOffset = 8;
2742  break;
2743  }
2744 
2745  case 9:
2746  {
2747  HOffset = 8;
2748  VOffset = 8;
2749  break;
2750  }
2751 
2752  default:
2753  {
2754  throw Exception("Error in GetOffsetValues - Link value wrong");
2755  }
2756  }
2757  Utilities->CallLogPop(674);
2758 }
2759 
2760 // ---------------------------------------------------------------------------
2761 
2762 bool TTrain::LowEntryValue(int EntryLink) const
2763 {
2764 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2765  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2766 */
2767  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2768  {
2769  return(true);
2770  }
2771  else
2772  {
2773  return(false);
2774  }
2775 }
2776 
2777 // ---------------------------------------------------------------------------
2778 
2779 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2780 {
2781  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2782  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2783  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2784  // default values
2785  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2786 
2787  TAllRoutes::TRouteType RouteType;
2788 
2789  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2790 
2791  TRect SourceRect, DestRect;
2792 
2793  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2794  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2795  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2796  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2797 
2798  TempGraphic->PixelFormat = pf8bit;
2799  TempGraphic->Width = 16;
2800  TempGraphic->Height = 16;
2801  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2802 
2803  if(TempElement.TrackType == Points)
2804  {
2805  TempGraphic->Assign(TempElement.GraphicPtr);
2806  TempGraphic->Transparent = true;
2807  TempGraphic->TransparentColor = Utilities->clTransparent;
2808  if(RouteType == TAllRoutes::AutoSigsRoute)
2809  {
2810  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2811  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2812  }
2813  else
2814  {
2815  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2816  }
2817  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2818  }
2819  else if(TempElement.TrackType == GapJump) // plot set gap
2820  {
2821  if(TempElement.SpeedTag == 88)
2822  {
2823  TempGraphic->Assign(RailGraphics->gl88set);
2824  }
2825  else if(TempElement.SpeedTag == 89)
2826  {
2827  TempGraphic->Assign(RailGraphics->gl89set);
2828  }
2829  else if(TempElement.SpeedTag == 90)
2830  {
2831  TempGraphic->Assign(RailGraphics->gl90set);
2832  }
2833  else if(TempElement.SpeedTag == 91)
2834  {
2835  TempGraphic->Assign(RailGraphics->gl91set);
2836  }
2837  else if(TempElement.SpeedTag == 92)
2838  {
2839  TempGraphic->Assign(RailGraphics->gl92set);
2840  }
2841  else if(TempElement.SpeedTag == 93)
2842  {
2843  TempGraphic->Assign(RailGraphics->bm93set);
2844  }
2845  else if(TempElement.SpeedTag == 94)
2846  {
2847  TempGraphic->Assign(RailGraphics->bm94set);
2848  }
2849  else if(TempElement.SpeedTag == 95)
2850  {
2851  TempGraphic->Assign(RailGraphics->gl95set);
2852  }
2853  TempGraphic->Transparent = true;
2854  TempGraphic->TransparentColor = Utilities->clTransparent;
2855  if(RouteType == TAllRoutes::AutoSigsRoute)
2856  {
2857  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2858  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2859  }
2860  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2861  }
2862  // new for version 0.6
2863  else if(TempElement.TrackType == SignalPost)
2864  {
2865  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2866  {
2867  for(int x = 0; x < 40; x++)
2868  {
2869  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2870  // need to stop aspect
2871  {
2872  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2873  break;
2874  }
2875  }
2876  }
2877  else // normal signal
2878  {
2879  TempGraphic->Assign(TempElement.GraphicPtr);
2880  // GraphicPtr set to normal signal in a signal track element
2881  }
2882  TempGraphic->Transparent = true;
2883  TempGraphic->TransparentColor = Utilities->clTransparent;
2884  if(RouteType == TAllRoutes::AutoSigsRoute)
2885  {
2886  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2887  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2888  }
2889  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2890  }
2891  else
2892  {
2893  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2894  // can't name points gaps or signals so 'else' OK
2895  bool FoundFlag;
2896  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2897  if(FoundFlag)
2898  {
2900  {
2901  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2902  TempGraphic->Assign(RailGraphics->bmName);
2903  TempGraphic->Transparent = true;
2904  TempGraphic->TransparentColor = Utilities->clTransparent;
2905  if(RouteType == TAllRoutes::AutoSigsRoute)
2906  {
2907  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2908  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2909  }
2910  else
2911  {
2912  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2913  }
2914  // draw track on top
2915  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2916  }
2917  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2918  {
2919  TempGraphic->Assign(TempElement.GraphicPtr);
2920  TempGraphic->Transparent = true;
2921  TempGraphic->TransparentColor = Utilities->clTransparent;
2922  // note that can't be an AutoSigsRoute
2923  // now overlay the LC central portion
2924  int BDVectorPos = -1; //not used
2925  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2926  {
2927  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2928  }
2929  else
2930  {
2931  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2932  }
2933  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2934  }
2935  else
2936  {
2937  TempGraphic->Assign(TempElement.GraphicPtr);
2938  TempGraphic->Transparent = true;
2939  TempGraphic->TransparentColor = Utilities->clTransparent;
2940  if(RouteType == TAllRoutes::AutoSigsRoute)
2941  {
2942  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2943  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2944  }
2945  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2946  }
2947  }
2948  else
2949  {
2950  TempGraphic->Assign(TempElement.GraphicPtr);
2951  TempGraphic->Transparent = true;
2952  TempGraphic->TransparentColor = Utilities->clTransparent;
2953  if(RouteType == TAllRoutes::AutoSigsRoute)
2954  {
2955  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2956  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2957  }
2958  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2959  }
2960  }
2961  delete TempGraphic;
2962  Utilities->CallLogPop(675);
2963 }
2964 
2965 // ---------------------------------------------------------------------------
2966 
2967 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2968 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2969 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2970 /*
2971 
2972  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2973  {
2974  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2975  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2976  TAllRoutes::TRouteType RouteType;
2977  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2978  // default values
2979  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2980  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2981  TRect SourceRect, DestRect, ScreenSourceRect;
2982  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2983 
2984  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2985  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2986  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2987 
2988  //add text & user graphics if any to *GraphicPtr prior to adding the track
2989  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2990  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2991  int Right = Left + 8;
2992  int Bottom = Top + 8;
2993  ScreenSourceRect.init(Left, Top, Right, Bottom);
2994  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2995  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2996 
2997  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2998  TempGraphic->PixelFormat = pf8bit;
2999  TempGraphic->Width = 16;
3000  TempGraphic->Height = 16;
3001 
3002  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
3003  SourceGraphic->PixelFormat = pf8bit;
3004  SourceGraphic->Width = 16;
3005  SourceGraphic->Height = 16;
3006  SourceGraphic->Transparent = true;
3007  SourceGraphic->TransparentColor = Utilities->clTransparent;
3008 
3009  if (TempElement.TrackType == Points)
3010  {
3011  TempGraphic->Assign(TempElement.GraphicPtr);
3012  TempGraphic->Transparent = true;
3013  TempGraphic->TransparentColor = Utilities->clTransparent;
3014  if (RouteType == TAllRoutes::AutoSigsRoute)
3015  {
3016  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3017  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3018  }
3019  else
3020  {
3021  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
3022  }
3023  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3024  }
3025  else if (TempElement.TrackType == GapJump) // plot set gap
3026  {
3027  if (TempElement.SpeedTag == 88)
3028  TempGraphic->Assign(RailGraphics->gl88set);
3029  else if (TempElement.SpeedTag == 89)
3030  TempGraphic->Assign(RailGraphics->gl89set);
3031  else if (TempElement.SpeedTag == 90)
3032  TempGraphic->Assign(RailGraphics->gl90set);
3033  else if (TempElement.SpeedTag == 91)
3034  TempGraphic->Assign(RailGraphics->gl91set);
3035  else if (TempElement.SpeedTag == 92)
3036  TempGraphic->Assign(RailGraphics->gl92set);
3037  else if (TempElement.SpeedTag == 93)
3038  TempGraphic->Assign(RailGraphics->bm93set);
3039  else if (TempElement.SpeedTag == 94)
3040  TempGraphic->Assign(RailGraphics->bm94set);
3041  else if (TempElement.SpeedTag == 95)
3042  TempGraphic->Assign(RailGraphics->gl95set);
3043  TempGraphic->Transparent = true;
3044  TempGraphic->TransparentColor = Utilities->clTransparent;
3045  if (RouteType == TAllRoutes::AutoSigsRoute) {
3046  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3047  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3048  }
3049  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3050  }
3051  // new for version 0.6
3052  else if (TempElement.TrackType == SignalPost)
3053  {
3054  if (TempElement.SigAspect == TTrackElement::GroundSignal)
3055  {
3056  for (int x = 0; x < 40; x++)
3057  {
3058  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
3059  // need to stop aspect
3060  {
3061  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
3062  break;
3063  }
3064  }
3065  }
3066  else // normal signal
3067  {
3068  TempGraphic->Assign(TempElement.GraphicPtr);
3069  // GraphicPtr set to normal signal in a signal track element
3070  }
3071  TempGraphic->Transparent = true;
3072  TempGraphic->TransparentColor = Utilities->clTransparent;
3073  if (RouteType == TAllRoutes::AutoSigsRoute) {
3074  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3075  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3076  }
3077  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3078  }
3079  else {
3080  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
3081  // can't name points gaps or signals so 'else' OK
3082  bool FoundFlag;
3083  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
3084  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
3085  if (FoundFlag)
3086  {
3087  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
3088  {
3089  GraphicPtr->Canvas->CopyRect(DestRect,
3090  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
3091  TempGraphic->Assign(RailGraphics->bmName);
3092  TempGraphic->Transparent = true;
3093  TempGraphic->TransparentColor = Utilities->clTransparent;
3094  if (RouteType == TAllRoutes::AutoSigsRoute)
3095  {
3096  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3097  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3098  }
3099  else
3100  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
3101  // draw track on top
3102  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3103  SourceRect);
3104  }
3105  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
3106  TempGraphic->Assign(TempElement.GraphicPtr);
3107  TempGraphic->Transparent = true;
3108  TempGraphic->TransparentColor = Utilities->clTransparent;
3109  // note that can't be an AutoSigsRoute
3110  // now overlay the LC central portion
3111  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
3112  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3113  SourceRect);
3114  }
3115  else {
3116  TempGraphic->Assign(TempElement.GraphicPtr);
3117  TempGraphic->Transparent = true;
3118  TempGraphic->TransparentColor = Utilities->clTransparent;
3119  if (RouteType == TAllRoutes::AutoSigsRoute) {
3120  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3121  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3122  }
3123  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3124  SourceRect);
3125  }
3126  }
3127  else {
3128  TempGraphic->Assign(TempElement.GraphicPtr);
3129  TempGraphic->Transparent = true;
3130  TempGraphic->TransparentColor = Utilities->clTransparent;
3131  if (RouteType == TAllRoutes::AutoSigsRoute) {
3132  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3133  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3134  }
3135  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3136  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
3137  }
3138  }
3139  delete TempGraphic;
3140  delete SourceGraphic;
3141  Utilities->CallLogPop();
3142  }
3143 */
3144 // ---------------------------------------------------------------------------
3145 
3146 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
3147 {
3148  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
3149  if(PlotElement[ArrayNumber] == -1)
3150  {
3151  Utilities->CallLogPop(676);
3152  return; // not plotted yet
3153  }
3154  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
3155  // set before plot so gap flashing stops first
3156  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3157  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
3158  // Only need to set ID for leading element, stays set until train finally leaves the element
3159  Plotted = true;
3160  Utilities->CallLogPop(677);
3161 }
3162 
3163 // ---------------------------------------------------------------------------
3164 
3165 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
3166 {
3167  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3168  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
3169 }
3170 
3171 // ---------------------------------------------------------------------------
3172 
3173 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
3174 {
3175  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
3176  HeadCode);
3177  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
3178  {
3179  Utilities->CallLogPop(678);
3180  return(true);
3181  }
3182  else
3183  {
3184  Utilities->CallLogPop(679);
3185  return(false);
3186  }
3187 }
3188 
3189 // ---------------------------------------------------------------------------
3190 
3191 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
3192 {
3193  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
3194  "," + HeadCode);
3195  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
3196  {
3197  Utilities->CallLogPop(680);
3198  return(true);
3199  }
3200  else
3201  {
3202  Utilities->CallLogPop(681);
3203  return(false);
3204  }
3205 }
3206 
3207 // ---------------------------------------------------------------------------
3208 
3209 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
3210 // test whether this train on a bridge on trackpos 0 & 1
3211 {
3212  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
3213  HeadCode);
3214  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
3215  {
3216  Utilities->CallLogPop(682);
3217  return(false);
3218  }
3219  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3221  {
3223  {
3224  throw Exception("Error, same train on two different bridge tracks");
3225  }
3226  else
3227  {
3228  Utilities->CallLogPop(684);
3229  return(true);
3230  }
3231  }
3232  else
3233  {
3234  Utilities->CallLogPop(685);
3235  return(false);
3236  }
3237 }
3238 
3239 // ---------------------------------------------------------------------------
3240 
3241 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
3242 // test whether this train on a bridge on trackpos 2 & 3
3243 {
3244  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
3245  HeadCode);
3246  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
3247  {
3248  Utilities->CallLogPop(686);
3249  return(false);
3250  }
3251  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3253  {
3254  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
3255  Utilities->CallLogPop(687);
3256  return(true);
3257  }
3258  else
3259  {
3260  Utilities->CallLogPop(688);
3261  return(false);
3262  }
3263 }
3264 
3265 // ---------------------------------------------------------------------------
3266 
3267 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3268 {
3269  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3270  AnsiString(EntryPos) + "," + HeadCode);
3271  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
3272 
3273  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
3274  if(Track->GapFlashFlag)
3275  {
3277  {
3280  Track->GapFlashFlag = false;
3281  }
3282  }
3283  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3284  {
3285  if(EntryPos == -1)
3286  {
3287  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3288  }
3289  if(EntryPos < 2)
3290  {
3292  }
3293  else
3294  {
3296  }
3297  }
3298  Utilities->CallLogPop(690);
3299 }
3300 
3301 // ---------------------------------------------------------------------------
3302 
3303 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3304 {
3305  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3306  AnsiString(EntryPos) + "," + HeadCode);
3307  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3308  {
3309  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3310  }
3311  else
3312  {
3313  if(EntryPos == -1)
3314  {
3315  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3316  }
3317  if(EntryPos < 2)
3318  {
3319  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = -1;
3320  }
3321  else
3322  {
3323  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 = -1;
3324  }
3325  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
3326  // i.e. other train on track 2&3
3327  {
3328  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
3329  }
3330  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
3331  // i.e. other train on track 1&2
3332  {
3333  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
3334  }
3335  else
3336  {
3337  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3338  }
3339  }
3340  Utilities->CallLogPop(691);
3341 }
3342 
3343 // ---------------------------------------------------------------------------
3344 
3345 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3346 {
3347  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3348  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3349  int LockedVectorNumber;
3350 
3351  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3352  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3353  {
3354  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3355  Utilities->CallLogPop(692);
3356  return;
3357  }
3358  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3359  // i.e other track is in a marked route
3360  // LinkPos doesn't have to be the entry position for the above check
3361  {
3362  TRect SourceRect, DestRect;
3363  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3364  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3365  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3366  // identify the route element for the other track
3367  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3368  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3369  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3370  int FirstELink, SecondELink = -1;
3371  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3372  // must be at least one
3373  if(RoutePair2.first > -1)
3374  {
3375  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3376  }
3377  TPrefDirElement RouteElement;
3378  // Graphics::TBitmap *RouteGraphic;
3379  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3380  // i.e. other track is in RoutePair2
3381  {
3382  if(SecondELink == -1)
3383  {
3384  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3385  }
3386  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3387  // error if both have same Link number
3388  {
3389  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3390  }
3391  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3392  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3393  }
3394  else // other track is in RoutePair1
3395  {
3396  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3397  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3398  }
3399  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3400  DestGraphic->PixelFormat = pf8bit;
3401  DestGraphic->Width = 8;
3402  DestGraphic->Height = 8;
3403  DestGraphic->Transparent = true;
3404  // has to be transparent or will overwrite the track that the train has just left
3405  DestGraphic->TransparentColor = Utilities->clTransparent;
3406  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3407  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3408  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3409  // plot locked route marker for other route if appropriate
3410  TPrefDirElement PrefDirElement; // holder for next call, unused
3411  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3412  if(StraddleValue == LeadMidLag)
3413  {
3415  PrefDirElement, LockedVectorNumber))
3416  {
3417  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3418  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3419  }
3420  }
3421  delete DestGraphic;
3422  }
3423  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3424  // also can only be a bridge or trains either have already or soon will crash
3425  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3426  {
3427  Utilities->CallLogPop(695);
3428  return;
3429  }
3430  if(ElementEntryPos > 1) // other train is on track 01
3431  {
3433  {
3435  }
3436  }
3437  else // other train is on track 23
3438  {
3440  {
3442  }
3443  }
3444  Utilities->CallLogPop(696);
3445 }
3446 
3447 // ---------------------------------------------------------------------------
3448 
3449 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3450 {
3451  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3452  AnsiString(EntryPos) + "," + HeadCode);
3453  int RouteNumber;
3454  bool WrongRoute = false;
3455  TPrefDirElement RouteElement;
3457  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3458 
3459  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3460  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3461  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3462  {
3463  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3464  {
3465  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3466  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3467  {
3468  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3469  {
3470  // don't call for stub end routes
3472  }
3473  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3474  Utilities->CallLogPop(697);
3475  return;
3476  }
3477  }
3478  // also need to check for a route on a crossing diagonal
3479  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3480  int LinkNumber = TrackElement.Link[EntryPos];
3481  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3482  {
3483  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3484  {
3485  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3486  bool LogActionErrorCalled = false;
3487  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3488  if(LinkNumber == 1)
3489  {
3490  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3491  {
3492  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3493  {
3494  // don't call for stub end routes
3496  LogActionErrorCalled = true;
3497  }
3498  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3499  }
3500  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3501  // not else in case have different routes on each diagonal, though shouldn't be possible
3502  {
3503  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3504  {
3505  // don't call for stub end routes
3507  }
3508  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3509  }
3510  }
3511 
3512  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3513  else if(LinkNumber == 3)
3514  {
3515  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3516  {
3517  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3518  {
3519  // don't call for stub end routes
3521  LogActionErrorCalled = true;
3522  }
3523  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3524  }
3525  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3526  // not else in case have different routes on each diagonal, though shouldn't be possible
3527  {
3528  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3529  {
3530  // don't call for stub end routes
3532  }
3533  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3534  }
3535  }
3536 
3537  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3538  else if(LinkNumber == 7)
3539  {
3540  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3541  {
3542  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3543  {
3544  // don't call for stub end routes
3546  LogActionErrorCalled = true;
3547  }
3548  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3549  }
3550  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3551  // not else in case have different routes on each diagonal, though shouldn't be possible
3552  {
3553  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3554  {
3555  // don't call for stub end routes
3557  }
3558  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3559  }
3560  }
3561 
3562  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3563  else if(LinkNumber == 9)
3564  {
3565  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3566  {
3567  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3568  {
3569  // don't call for stub end routes
3571  LogActionErrorCalled = true;
3572  }
3573  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3574  }
3575  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3576  // not else in case have different routes on each diagonal, though shouldn't be possible
3577  {
3578  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3579  {
3580  // don't call for stub end routes
3582  }
3583  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3584  }
3585  }
3586  }
3587  }
3588  Utilities->CallLogPop(698);
3589  return; // no route on other track or no other track
3590  }
3591  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3592  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3593  {
3594  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3595  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3596  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3597  {
3598  if(RouteElement.GetELinkPos() == EntryPos)
3599  {
3600  Utilities->CallLogPop(699);
3601  return; // right direction
3602  }
3603  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3604  {
3605  Utilities->CallLogPop(700);
3606  return; // right direction (points)
3607  }
3608  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3609  {
3610  Utilities->CallLogPop(701);
3611  return; // right direction (points)
3612  }
3613  else if(RouteElement.GetXLinkPos() == EntryPos)
3614  {
3615  WrongRoute = true;
3616  break; // wrong direction
3617  }
3618  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3619  {
3620  WrongRoute = true;
3621  break; // wrong direction
3622  }
3623  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3624  {
3625  WrongRoute = true;
3626  break; // wrong direction
3627  }
3628  }
3629  }
3630  if(!WrongRoute)
3631  {
3632  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3633  }
3634  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3635  {
3636  // don't call for stub end routes
3638  }
3639  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3640  Utilities->CallLogPop(703);
3641 }
3642 
3643 // ---------------------------------------------------------------------------
3644 
3645 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3646 {
3647  if(BackgroundColour == NewBackgroundColour)
3648  {
3649  return; // don't replot if already correct
3650 
3651  }
3652  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3653  bool ColourError = false, ColourError2 = false;
3654 
3655  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3656  if(ColourError)
3657  {
3658  ColourError2 = true;
3659  }
3660  for(int x = 0; x < 4; x++)
3661  {
3662  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3663  if(ColourError)
3664  {
3665  ColourError2 = true;
3666  }
3667  }
3668  if(ColourError2)
3669  {
3671  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3672  }
3673  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3674  // of motion
3675  for(int x = 0; x < 4; x++)
3676  {
3677  PlotTrainGraphic(6, x, Disp);
3678  }
3679  BackgroundColour = NewBackgroundColour;
3680  Display->Update();
3681  // need to keep this since Update() not called for PlotSmallOutput as too slow
3682  Utilities->CallLogPop(704);
3683 }
3684 
3685 // ---------------------------------------------------------------------------
3686 
3687 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3688 /*
3689 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3690 
3691 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3692 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3693 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3694 full-element moves.
3695 
3696 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3697 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3698 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3699 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3700 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3701 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3702 In this case set the brake rate to maximum to stop as soon as possible.
3703 
3704 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3705 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3706 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3707 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3708 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3709 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3710 
3711 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3712 first to see whether buffers or continuation) in turn is examined: first the length of the
3713 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3714 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3715 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3716 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3717 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3718 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3719 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3720 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3721 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3722 siding then again emeregency braking may be necessary and a crash may result.
3723 
3724 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3725 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3726 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3727 buffer, then the train accelerates for half the element and brakes for the other half.
3728 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3729 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3730 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3731 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3732 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3733 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3734 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3735 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3736 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3737 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3738 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3739 
3740 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3741 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3742 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3743 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3744 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3745 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3746 MaxBrakeRate.
3747 
3748 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3749 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3750 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3751 
3752 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3753 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3754 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3755 
3756 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3757 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3758 when Straddle == LeadMidLag
3759 */
3760 {
3761  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3762  AnsiString(EntryPos) + "," + HeadCode);
3763  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3764  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3765  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3766  TrainInFrontInSignallerModeFlag = false;
3767  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3768  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3769  bool SignallerStopRequired = false;
3770 
3772  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3773 
3774  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3775 
3776  OneLengthAccelDecel = false;
3777  BrakeRate = 0;
3778 
3779 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3780  if(CurrentTrackVectorPosition > -1)
3781  {
3782  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3783  {
3784  if((EntryPos == 0) || (EntryPos == 2))
3785  {
3786  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3787  {
3788  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3789  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3790  }
3791  else
3792  {
3793  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3794  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3795  }
3796  }
3797  else if(EntryPos == 1)
3798  {
3799  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3800  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3801  }
3802  else // == 3
3803  {
3804  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3805  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3806  }
3807  }
3808  else
3809  {
3810  if(EntryPos > 1)
3811  {
3812  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3813  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3814  }
3815  else
3816  {
3817  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3818  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3819  }
3820  }
3821  EntryHalfLength = CurrentElementHalfLength;
3822  FrontElementLength = 2 * CurrentElementHalfLength;
3823  }
3824  else
3825  {
3826  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3827  }
3828  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3829  {
3830  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3831  }
3832  // check if zero entry speed with another train directly in front & if so remain stopped
3833  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3834  {
3835  EntrySpeed = 0;
3836  ExitSpeedHalf = 0;
3837  ExitSpeedFull = 0;
3838  MaxExitSpeed = 0;
3839  BrakeRate = 0;
3840  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3841  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3842  StoppedForTrainInFront = true;
3843  Utilities->CallLogPop(705);
3844  return;
3845  }
3846  // new at v2.4.0 - check for stopped and zero power
3847  if((EntrySpeed < 1) && PowerAtRail < 1)
3848  {
3849  EntrySpeed = 0;
3850  ExitSpeedHalf = 0;
3851  ExitSpeedFull = 0;
3852  MaxExitSpeed = 0;
3853  BrakeRate = 0;
3854  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3855  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3856  StoppedWithoutPower = true;
3857  Utilities->CallLogPop(2125);
3858  return;
3859  }
3860 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3861  if(BeingCalledOn)
3862  {
3863  LimitingSpeed = CallOnMaxSpeed;
3864  }
3865  else
3866  {
3867  LimitingSpeed = MaximumSpeedLimit;
3868  }
3869  if(LimitingSpeed > FrontElementSpeedLimit)
3870  {
3871  LimitingSpeed = FrontElementSpeedLimit;
3872  }
3873  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3874  {
3875  LimitingSpeed = MaxRunningSpeed;
3876  }
3877  FrontElementMaxSpeed = LimitingSpeed;
3878 
3879 /*
3880  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3881  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3882  (2) V/3.6 = U/3.6 - FT;
3883  (3) S = UT/3.6 - 0.5FT^2
3884  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3885 
3886  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3887  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3888  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3889  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3890  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3891 
3892  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3893  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3894  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3895 */
3896 
3897 // check if running past a red signal without permission
3898  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) &&
3899  (Track->TrackElementAt(353, CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal &&
3900  !Track->TrackElementAt(1553, CurrentTrackVectorPosition).CallingOnSet)
3901  { //CallingOnSet added at v2.14.0
3902  SPADFlag = true; // user has to intervene to reset & restart after spad
3903  }
3904  if(!SPADFlag)
3905  {
3906  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3907  // to begin with calc the maximum exit speed (assumes accelerating) and then reduce it if necessary
3908  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3909  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3910  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3911  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3912 
3913  double ExitSpeedAtMaxBraking;
3914  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3915  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3917  {
3918  ExitSpeedAtMaxBraking = 0;
3919  }
3920  else
3921  {
3922  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3923  }
3924  double SpeedToUse;
3925  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3926  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3927  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3928  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3929  {
3930  SpeedToUse = ExitSpeedAtMaxBraking;
3931  }
3932  else
3933  {
3934  SpeedToUse = LimitingSpeed;
3935  }
3936  if(ExitSpeedFull > SpeedToUse)
3937  {
3938  ExitSpeedFull = SpeedToUse;
3939  }
3940  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3941  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3942 
3943  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3944  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3945  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3946 
3947  do
3948  {
3949  RedSignalFlag = false;
3950  BuffersFlag = false;
3951  StationFlag = false;
3952  BuffersOrContinuationNowFlag = false;
3953  ContinuationNextFlag = false;
3954  // have to reset this after the above test
3955  // add current element length to CumulativeLength
3956  CumulativeLength += (2 * CurrentElementHalfLength);
3957  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3958  {
3959  SignallerStopRequired = true;
3960  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3961  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3962  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3963  if(SignallerStopBrakeRate < TempBR)
3964  {
3965  SignallerStopBrakeRate = TempBR;
3966  }
3967  }
3968  // first check for stops within the length of the current element, where don't want any more checks & don't want
3969  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3970  // during the last loop when the NextTrackVectorPosition was the signal.
3971 
3972  // check if current element is a buffer
3973  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3974  {
3975  // no need to add in the length of this element to CumulativeLength as already included
3976  BuffersFlag = true;
3977  }
3978  // check if current element is a station stop
3979  if(TrainMode == Timetable)
3980  {
3981  bool StopRequired = false;
3982  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3983  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3984  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3985  {
3986  // no need to add in the length of element to CumulativeLength
3987  if(StopRequired)
3988  {
3989  StationFlag = true;
3990  }
3991  }
3992  }
3993  else
3994  {
3995  StationFlag = false;
3996  }
3997  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3998  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3999  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4000  {
4001  BuffersOrContinuationNowFlag = true;
4002  }
4003  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
4004  {
4005  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
4006  {
4007  if((EntryPos == 0) || (EntryPos == 2))
4008  {
4009  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
4010  {
4011  ExitPos = 1;
4012  }
4013  else
4014  {
4015  ExitPos = 3;
4016  }
4017  }
4018  else
4019  {
4020  ExitPos = 0;
4021  }
4022  }
4023  else
4024  {
4025  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4026  }
4027  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
4028  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4029  if(NextTrackVectorPosition > -1)
4030  {
4031  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
4032  // this test & section added at v0.6
4033  {
4034  if((NextEntryPos == 0) || (NextEntryPos == 2))
4035  {
4036  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
4037  {
4038  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
4039  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
4040  }
4041  else
4042  {
4043  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
4044  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
4045  }
4046  }
4047  else if(NextEntryPos == 1)
4048  {
4049  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
4050  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
4051  }
4052  else // == 3
4053  {
4054  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
4055  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
4056  }
4057  }
4058  else
4059  {
4060  if(NextEntryPos > 1)
4061  {
4062  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
4063  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
4064  }
4065  else
4066  {
4067  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
4068  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
4069  }
4070  }
4071  }
4072  else
4073  {
4074  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
4075  }
4076  // now check for stops, first cover those where don't want to add in length of next element
4077  // check if next element is a red signal - Attr 0,
4078  // note that this doesn't apply to trains stopped at a red signal since the signal position is
4079  // CurrentTrackVectorPosition not NextTrackVectorPosition
4080  bool StopRequired;
4081  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
4082  {
4083  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
4084  {
4085  // no need to add in the length of element to CumulativeLength
4086  RedSignalFlag = true;
4087  }
4088  // next element is a red signal
4089  }
4090  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
4091  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
4092  // at least one platform element free
4094  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
4095  NextEntryPos, TrainID))
4096  {
4097  // no need to add in the length of element to CumulativeLength
4098  if(StopRequired)
4099  {
4100  StationFlag = true;
4101  }
4102  }
4103  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
4104  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
4105  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
4106  {
4107  // no need to add in the length of element to CumulativeLength
4108  TrainInFrontInSignallerModeFlag = true;
4109  }
4110  // check if next element is a buffer
4111  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
4112  {
4113  // need to add in the length of that element to CumulativeLength
4114  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
4115  BuffersFlag = true;
4116  }
4117  // check if next element is a station stop
4119  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
4120  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
4121  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
4122  {
4123  // need to add in the length of that element to CumulativeLength if a stop required
4124  if(StopRequired)
4125  {
4126  StationFlag = true;
4127  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
4128  }
4129  }
4130  }
4131  //now can decide whether need to stop over CumulativeLength
4132  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
4133  {
4134  // have to come to a stop over CumulativeLength
4135  if(CumulativeLength == FrontElementLength)
4136  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
4137  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
4138  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
4139  // and if less than EntrySpeed then skip this section (don't need any acceleration)
4140  // if not calc speed at halfway point & if less than above set half speed to this value;
4141  // use constant acceleration in calculating half time point
4142  {
4143  MaxExitSpeed = 0;
4144  double MaxHalfSpeed;
4145  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
4146  // have to halve the element length, & can't be zero or negative so no need to test
4147  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4148  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
4149  {
4150  MaxHalfSpeed = FrontElementMaxSpeed;
4151  }
4152  else
4153  {
4154  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4155  }
4156  if(MaxHalfSpeed > (2 * EntrySpeed))
4157  // use 2x to prevent kangarooing at last element when had
4158  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
4159  {
4160  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4161  0.333334);
4162  bool HalfSpeedLimited = false;
4163  if(MaxHalfSpeed < ExitSpeedHalf)
4164  {
4165  ExitSpeedHalf = MaxHalfSpeed;
4166  HalfSpeedLimited = true;
4167  }
4168  if(PowerAtRail > 1)
4169  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4170  {
4171  // [km/h/3.6 = m/s]
4172  ExitTimeHalf =
4173  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4174  }
4175  else
4176  {
4177  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4178  }
4179  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
4180  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
4181  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
4182  // by a long braking period
4183  ExitSpeedFull = 0;
4184  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
4185  if(TempBrakeRate > MaxBrakeRate)
4186  {
4187  TempBrakeRate = MaxBrakeRate;
4188  }
4189  // shouldn't be but leave in anyway
4190  if(TempBrakeRate > BrakeRate)
4191  {
4192  BrakeRate = TempBrakeRate;
4193  }
4194  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4195  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
4196  if(HalfSpeedLimited)
4197  // this is the change referred to above
4198  {
4199  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
4200  ExitTimeHalf = EntryTime + BrakingTime;
4201  ExitTimeFull = ExitTimeHalf + BrakingTime;
4202  }
4203  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
4204  Utilities->CallLogPop(1095);
4205  return;
4206  }
4207  }
4208  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
4209  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
4210  // calc th, tf, sh, & sf
4211  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
4212  if(TempBrakeRate > MaxBrakeRate)
4213  {
4214  TempBrakeRate = MaxBrakeRate;
4215  }
4216  if(TempBrakeRate > BrakeRate)
4217  {
4218  BrakeRate = TempBrakeRate;
4219  }
4220  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4221  if(SignallerStopRequired)
4222  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
4223  {
4225  {
4227  // this prevents the brakerate from reducing for a signaller stop
4228  // regardless of other conditions that may change as progress round the loop
4229  }
4230  }
4232  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
4233  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
4234  {
4236  }
4237  int TempMaxExitSpeed;
4238  // calc current value & if less than MaxExitSpeed set that to this
4239  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
4240  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4241  {
4242  MaxExitSpeedAtHalfBraking = 0;
4243  }
4244  else
4245  {
4246  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4247  }
4248  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
4249  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
4250  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4251  {
4252  TempMaxExitSpeed = FrontElementMaxSpeed;
4253  }
4254  else
4255  {
4256  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4257  }
4258  if(TempMaxExitSpeed < MaxExitSpeed)
4259  {
4260  MaxExitSpeed = TempMaxExitSpeed;
4261  }
4262  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
4263  // Cumulativelength, and Cumulativelength
4264 
4265  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
4266  {
4267  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4268  if(ExitSpeedHalfSquared < 10)
4269  {
4270  ExitSpeedHalf = 0;
4271  }
4272  else
4273  {
4274  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4275  }
4276  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4277  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4278  if(ExitSpeedFullSquared < 10)
4279  {
4280  ExitSpeedFull = 0;
4281  }
4282  else
4283  {
4284  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4285  }
4286  if((StationFlag) && (CumulativeLength == FrontElementLength))
4287  {
4288  ExitSpeedFull = 0;
4289  // force a stop for station (not for buffers or red signal)
4290  }
4291  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4292  }
4293  // new condition at v2.4.0
4294  else if(PowerAtRail <= 1)
4295  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4296  // avoid using AValue in denominator or have excessively long times
4297  {
4298  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4299  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4300  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4301 
4302  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4303  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4304  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4305  }
4306  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4307  // without the power need above condition or have hours of delay times, above added at v2.4.0
4308  {
4309  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4310  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4311  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4312  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4313  BrakeRate = 0;
4314  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4315  0.333334);
4316  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4317  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4319  // can accelerate continually over the half length
4320  {
4321  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4322  / 86400.0);
4324  // can accelerate continually over the full length
4325  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4326  {
4327  ExitTimeFull =
4328  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4329  }
4330  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4331  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4332  {
4333  // added at v0.6 as a safeguard
4334  if(MaxExitSpeed < EntrySpeed)
4335  {
4337  }
4338  // to prevent DeltaExitTimeToMaxInSecs being negative
4339  if(MaxExitSpeed < 1)
4340  {
4341  MaxExitSpeed = 1;
4342  }
4343  // to prevent divide by zero error
4344  // below as was
4346  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4347  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4348  (1.5 * AValue * AValue);
4349  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4350  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4351  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4352  }
4353  }
4354  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4355  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4356  // second halves of the element
4357  {
4358  // added at v0.6 as a safeguard
4359  if(MaxExitSpeed < EntrySpeed)
4360  {
4362  }
4363  // to prevent DeltaExitTimeToMaxInSecs being negative
4364  if(MaxExitSpeed < 1)
4365  {
4366  MaxExitSpeed = 1; // to prevent divide by zero error
4367  }
4368  // below as was
4370  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4371  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4372  (1.5 * AValue * AValue);
4373  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4374  // remaining distance to half length
4375  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4376  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4378  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4379  }
4380  }
4381  Utilities->CallLogPop(706);
4382  return;
4383  }
4384  else
4385  {
4386  if(!BuffersOrContinuationNowFlag)
4387  {
4388  if(NextSpeedLimit < LimitingSpeed)
4389  {
4390  LimitingSpeed = NextSpeedLimit;
4391  }
4392  }
4393  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4394  int TempMaxExitSpeed;
4395  // calc current value & if less than MaxExitSpeed set that to this
4396  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4397  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4398  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4399  {
4400  MaxExitSpeedAtHalfBraking = 0;
4401  }
4402  else
4403  {
4404  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4405  }
4406  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4407  {
4408  TempMaxExitSpeed = FrontElementMaxSpeed;
4409  }
4410  else
4411  {
4412  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4413  }
4414  if(TempMaxExitSpeed < MaxExitSpeed)
4415  {
4416  MaxExitSpeed = TempMaxExitSpeed;
4417  }
4418  // MaxExitSpeed is an external variable & this can reduce its value
4419  if(EntrySpeed > LimitingSpeed)
4420  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4421  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4422  {
4423  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4424  if(TempBrakeRate > MaxBrakeRate)
4425  {
4426  TempBrakeRate = MaxBrakeRate;
4427  }
4428  // shouldn't be for speedlimits since all known in advance, but include anyway
4429  if(TempBrakeRate > BrakeRate)
4430  {
4431  BrakeRate = TempBrakeRate;
4432  }
4433  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4434  }
4435  }
4436  if(!BuffersOrContinuationNowFlag)
4437  {
4438  CurrentTrackVectorPosition = NextTrackVectorPosition;
4439  EntryPos = NextEntryPos;
4440  CurrentElementHalfLength = NextElementHalfLength;
4441  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4442  {
4443  ContinuationNextFlag = true;
4444  }
4445  }
4446  }
4447  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4449  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4450  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4451  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4452  // stopping distance after it.
4453 
4454  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4455  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4456 
4457  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4458  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4459  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4460  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4461  // too late
4462 
4463  // set final braking or acc'n speed & time values
4464  if(BrakeRate > 0.01)
4465  {
4466  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4467  if(ExitSpeedHalfSquared < 10)
4468  {
4469  ExitSpeedHalf = 0;
4470  }
4471  else
4472  {
4473  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4474  }
4475  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4476  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4477  if(ExitSpeedFullSquared < 10)
4478  {
4479  ExitSpeedFull = 0;
4480  }
4481  else
4482  {
4483  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4484  }
4485  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4486  }
4487  else
4488  {
4489  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4490  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4491  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4492  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4493 
4494  BrakeRate = 0;
4495  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4496  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4497  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4499  {
4500  if(PowerAtRail > 1)
4501  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4502  {
4503  // [km/h/3.6 = m/s]
4504  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4505  / 86400.0);
4506  }
4507  else
4508  {
4509  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4510  }
4512  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4513  {
4514  if(PowerAtRail > 1)
4515  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4516  {
4517  // [km/h/3.6 = m/s]
4518  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4519  / 86400.0);
4520  }
4521  else
4522  {
4523  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4524  }
4525  }
4526  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4527  {
4528  // added at v0.6 as a safeguard
4529  if(MaxExitSpeed < EntrySpeed)
4530  {
4532  }
4533  // to prevent DeltaExitTimeToMaxInSecs being negative
4534  if(MaxExitSpeed < 1)
4535  {
4536  MaxExitSpeed = 1; // to prevent divide by zero error
4537  }
4538  // below as was
4540  double DeltaExitTimeToMaxInSecs;
4541  double DistanceToMax;
4542  if(PowerAtRail > 1) // added at v2.4.0
4543  {
4544  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4545  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4546  (1.5 * AValue * AValue);
4547  }
4548  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4549  {
4550  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4551  // these not really accurate but will be good enough
4552  DistanceToMax = EntryHalfLength;
4553  }
4554  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4555  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4556  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4557  }
4558  }
4559  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4560  {
4561  // added at v0.6 as a safeguard
4562  if(MaxExitSpeed < EntrySpeed)
4563  {
4565  }
4566  // to prevent DeltaExitTimeToMaxInSecs being negative
4567  if(MaxExitSpeed < 1)
4568  {
4569  MaxExitSpeed = 1; // to prevent divide by zero error
4570  }
4571  // below as was
4573  double DeltaExitTimeToMaxInSecs;
4574  double DistanceToMax;
4575  if(PowerAtRail > 1) // added at v2.4.0
4576  {
4577  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4578  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4579  (1.5 * AValue * AValue);
4580  }
4581  else
4582  {
4583  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4584  // these not really accurate but will be good enough
4585  DistanceToMax = EntryHalfLength / 2;
4586  }
4587  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4588  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4589  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4591  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4592  }
4593  }
4594  }
4595 
4596  else // SPADFlag set
4597  {
4599  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4600  if(ExitSpeedHalfSquared < 10)
4601  {
4602  ExitSpeedHalf = 0;
4603  }
4604  else
4605  {
4606  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4607  }
4608  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4609  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4610  if(ExitSpeedFullSquared < 10)
4611  {
4612  ExitSpeedFull = 0;
4613  }
4614  else
4615  {
4616  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4617  }
4618  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4619 
4620  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4621  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4622  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4623  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4624  // will be the stopping speed.
4625  if(ExitSpeedFull > 0)
4626  {
4627  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4628  {
4629  if((EntryPos == 0) || (EntryPos == 2))
4630  {
4631  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4632  {
4633  ExitPos = 1;
4634  }
4635  else
4636  {
4637  ExitPos = 3;
4638  }
4639  }
4640  else
4641  {
4642  ExitPos = 0;
4643  }
4644  }
4645  else
4646  {
4647  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4648  }
4649  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4650  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4651  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4652  {
4653  int NextElementLength;
4654  if(NextEntryPos > 1)
4655  {
4656  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4657  }
4658  else
4659  {
4660  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4661  }
4662  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4663  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4664  {
4665  ExitSpeedFull = 0;
4666  }
4667  }
4668  }
4669  }
4670  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4671  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4672  {
4673  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4674  if(LeadElement > -1)
4675  {
4677  // don't stop on a continuation either entering or leaving
4678  {
4681  MaxExitSpeed = LimitingSpeed;
4682  BrakeRate = 0;
4683  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4684  // assume length is 50m for ease of calc
4685  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4686  Utilities->CallLogPop(2126);
4687  return;
4688  }
4689  }
4690  else if(MidElement > -1)
4691  {
4693  {
4696  MaxExitSpeed = LimitingSpeed;
4697  BrakeRate = 0;
4698  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4699  // assume length is 50m for ease of calc
4700  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4701  Utilities->CallLogPop(2127);
4702  return;
4703  }
4704  }
4705  else if(LagElement > -1)
4706  {
4708  {
4711  MaxExitSpeed = LimitingSpeed;
4712  BrakeRate = 0;
4713  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4714  // assume length is 50m for ease of calc
4715  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4716  Utilities->CallLogPop(2128);
4717  return;
4718  }
4719  }
4720  if(EntrySpeed > 7.5) // keep going for at least another element
4721  {
4722  ExitSpeedHalf = EntrySpeed - 2.5;
4723  ExitSpeedFull = EntrySpeed - 5;
4724  MaxExitSpeed = LimitingSpeed;
4725  BrakeRate = 0;
4726  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4727  // assume length is 50m for ease of calc
4728  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4729  Utilities->CallLogPop(2129);
4730  return;
4731  }
4732  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4733  {
4734  // will appear to have slowed at steady rate
4735  ExitSpeedHalf = 0;
4736  ExitSpeedFull = 0;
4737  MaxExitSpeed = LimitingSpeed;
4738  BrakeRate = 0;
4739  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4740  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4741  StoppedWithoutPower = true;
4742  Utilities->CallLogPop(2130);
4743  return;
4744  }
4745  }
4746  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4747  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4748  Utilities->CallLogPop(707);
4749 }
4750 // ---------------------------------------------------------------------------
4751 /*
4752  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4753  {
4754  int NextExitPos;
4755  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4756  if(TimetableVector.empty()) return false;
4757  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4758  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4759  {
4760  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4761  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4762  {
4763  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4764  }
4765  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4766  {
4767  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4768  }
4769  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4770  NextElement = TempElement;
4771  }
4772  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4773  if(NextElement.TrackType == Buffers) return true;
4774  return false;//shouldn't reach here but include to prevent compiler return warning
4775  }
4776 */
4777 // ---------------------------------------------------------------------------
4778 
4779 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4780 /*
4781  returns the number by which the train ActionVectorEntryPtr needs
4782  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4783  actions when a stop or pass location has been reached before other timetabled events have been carried out. If can't find it, or Name
4784  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4785  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4786  or pass (false) the location.
4787 */{
4788  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4789  Stop = false;
4790  if(TimetableFinished || (Name == ""))
4791  {
4792  Utilities->CallLogPop(957);
4793  return(-1);
4794  }
4796 /*added the following check at v2.11.1 because of a fault found in Kevin Smith's railway (notified 02/01/22). CH01 started at
4797 Chester behind the stop position, but when it departed and this function was called it found Chester again with no cdt
4798 before it (it went round the Liverpool Loop), so it stopped again when it reached the stop position and reported all intermediate stops as having been
4799 missed. This check is for the train just having departed from the station in question, and if it has then any further stations
4800 with the same name are ignored - i.e. it stops a train from stopping at the same station twice in succession.
4801 */
4802  if(Ptr > &TrainDataEntryPtr->ActionVector.at(0))
4803  {
4804  Ptr--;
4805  if((Ptr->DepartureTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4806  {
4807  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4808  {
4809  Utilities->CallLogPop(2444);
4810  return(-1);
4811  }
4812  }
4813  }
4814  // start looking from current pointer position
4815  for(TActionVectorEntry *Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4816  {
4817  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4818  {
4819  break;
4820  }
4821  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4822  {
4823  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4824  {
4825  Stop = true;
4826  Utilities->CallLogPop(960);
4827  return (Ptr - ActionVectorEntryPtr);
4828  }
4829  }
4830  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4831  {
4832  Utilities->CallLogPop(1517);
4833  return (Ptr - ActionVectorEntryPtr);
4834  }
4835  }
4836  Utilities->CallLogPop(959);
4837  return(-1); // not found a valid entry
4838 }
4839 
4840 // ---------------------------------------------------------------------------
4841 
4843 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4844  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4845  Ignores the call-on signal.
4846 */{
4847  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4848  int ReturnVal = 0;
4849  int ElementCount = 0;
4850 /* dropped at v2.12.0 as takes up a great deal of time unnecessarily - substitute 1000 elements instead and return true (very unlikley to need to search this far [10km at min length])
4851  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4852  {
4853  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4854  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4855  }
4856 */
4857  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4858  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4859 
4860  while(true)
4861  {
4862  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4863  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4864  {
4865  ReturnVal = 1;
4866  break;
4867  }
4868  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4869  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4870  {
4871  ReturnVal = 2;
4872  break;
4873  }
4874  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4875  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4876  {
4877  ReturnVal = 3;
4878  break;
4879  }
4880 /* not needed at and after v2.12.0, see above
4881  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388, CurrentTrackVectorPosition).TrackType == Crossover))
4882  {
4883  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4884  // must be a loop - reached same point as examined earlier
4885  {
4886  ReturnVal = 4;
4887  break;
4888  }
4889  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4890  {
4891  ReturnVal = 4;
4892  break;
4893  }
4894  }
4895  else
4896  {
4897  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526, CurrentTrackVectorPosition).TempTrackMarker23))
4898  {
4899  ReturnVal = 4;
4900  break;
4901  }
4902  }
4903  if(EntryPos < 2)
4904  {
4905  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4906  }
4907  else
4908  {
4909  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4910  }
4911 */
4912 
4913  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4914  {
4915  if((EntryPos == 0) || (EntryPos == 2))
4916  {
4917  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4918  {
4919  ExitPos = 1;
4920  }
4921  else
4922  {
4923  ExitPos = 3;
4924  }
4925  }
4926  else
4927  {
4928  ExitPos = 0;
4929  }
4930  }
4931  else
4932  {
4933  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4934  }
4935  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4936  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4937  CurrentTrackVectorPosition = NextTrackVectorPosition;
4938  EntryPos = NextEntryPos;
4939  ElementCount++;
4940  if(ElementCount > 1000)
4941  {
4942  ReturnVal = 4;
4943  break;
4944  }
4945  }
4946  if(ReturnVal == 1)
4947  {
4948  Utilities->CallLogPop(708);
4949  return(false);
4950  }
4951  if(ReturnVal == 2)
4952  {
4953  Utilities->CallLogPop(709);
4954  return(true);
4955  }
4956  if(ReturnVal == 3)
4957  {
4958  Utilities->CallLogPop(946);
4959  return(true);
4960  }
4961  if(ReturnVal == 4)
4962  {
4963  Utilities->CallLogPop(947);
4964  return(true);
4965  }
4966  else
4967  {
4968  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4969  }
4970 }
4971 
4972 // ---------------------------------------------------------------------------
4973 
4975 /*
4976  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4977  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4978  change of direction (cdt), remaining here (Frh), or under signaller control);
4979  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4980  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4981  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4982  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] l) no existing route conflicts with the route into the platform,
4983  m) not failed or stopped without power
4984  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4985  change points outside the route or have a route conflict if another route is set.
4986 */{
4987  if(Track->RouteFlashFlag || TrainFailed || StoppedWithoutPower) //failed & no power conditions added at v2.10.0
4988  {
4989  return(false); // don't want to create a new route from the stop signal if one is already in construction & can't call on if failed or no power
4990  }
4991  // some of the callingon route elements may be involved
4992  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4993  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4994  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4995  int RouteStartPosition;
4996  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4997  int PlatformPosition;
4998  // the track vector position of the first stop platfrom
4999  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
5000  // not used here
5001  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
5002  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
5003  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
5004  // must be stopped at a signal but not at a location & still in timetable (a)
5006  // no need to check for SignallerStopped as this function only called in Timetable mode
5007  {
5008  Utilities->CallLogPop(711);
5009  return(false);
5010  }
5011  while(true)
5012  {
5013  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
5014  // don't look further than 4km ahead (j)
5015  if(Distance > (4000 + LeadElementDistance))
5016  {
5017  Utilities->CallLogPop(967);
5018  return(false);
5019  }
5020  // if find another train on an element in front, before find a valid platform, return false (c)
5021  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
5022  {
5023  Utilities->CallLogPop(713);
5024  return(false);
5025  }
5026  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
5027  // be facing later on)
5028  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
5029  {
5030  // get LeadElement, if -1 return (could be exiting at continuation) (i)
5031  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
5032  if(OtherTrain.LeadElement == -1)
5033  {
5034  Utilities->CallLogPop(714);
5035  return(false);
5036  }
5037  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
5038  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
5039  {
5040  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
5041  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
5042  (OtherTrain.TrainMode == Signaller))
5043  {
5044  break;
5045  }
5046  else
5047  {
5048  Utilities->CallLogPop(955);
5049  return(false);
5050  }
5051  }
5052  else // (h)
5053  {
5054  break;
5055  }
5056  }
5057  // if reach buffers or exit continuation return false (can set route)
5058  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
5059  {
5060  Utilities->CallLogPop(716);
5061  return(false);
5062  }
5063  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
5064  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
5066  {
5067  Utilities->CallLogPop(717);
5068  return(false);
5069  }
5070  // if reach a location that isn't in timetable return false - drop this as still can't set a route
5071 /*
5072  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
5073  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
5074  {
5075  Utilities->CallLogPop(718);
5076  return false;
5077  }
5078 */
5079  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
5080  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
5081  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
5082  {
5083  if(StopRequired)
5084  {
5085  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
5086  {
5087  if(!PlatformFoundFlag)
5088  {
5089  PlatformPosition = CurrentTrackVectorPosition;
5090  }
5091  // ensure this only set once at first valid platform position - the unrestricted route will end here
5092  PlatformFoundFlag = true;
5093  }
5094  }
5095  }
5096  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
5097  // train has to be at station but that has to be before the next forward signal
5098 /*
5099  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
5100  {
5101  Utilities->CallLogPop(719);
5102  return false;
5103  }
5104 */
5105  // make sure points are followed correctly (d) & set ExitPos
5106  if(CurrentTrackElement.TrackType == Points)
5107  {
5108  if((EntryPos == 0) || (EntryPos == 2))
5109  {
5110  if(CurrentTrackElement.Attribute == 0)
5111  {
5112  ExitPos = 1;
5113  }
5114  else
5115  {
5116  ExitPos = 3;
5117  }
5118  }
5119  if(EntryPos == 1)
5120  {
5121  if(CurrentTrackElement.Attribute == 0)
5122  {
5123  ExitPos = 0;
5124  }
5125  else
5126  {
5127  Utilities->CallLogPop(720);
5128  return(false);
5129  }
5130  }
5131  if(EntryPos == 3)
5132  {
5133  if(CurrentTrackElement.Attribute == 1)
5134  {
5135  ExitPos = 0;
5136  }
5137  else
5138  {
5139  Utilities->CallLogPop(721);
5140  return(false);
5141  }
5142  }
5143  }
5144  else
5145  {
5146  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
5147  }
5148  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
5149  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
5150  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
5151  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
5152  if(ElementNumber < 2)
5153  {
5154  SkipRouteCheck = true;
5155  }
5156  else
5157  {
5158  SkipRouteCheck = false;
5159  }
5160  if(ElementNumber == 1) // the stop signal
5161  {
5162  RouteStartPosition = CurrentTrackVectorPosition;
5163  }
5164 /*
5165  if(ElementNumber == 2)
5166  {
5167  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
5168  else AutoSigs = false;
5169  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
5170  }
5171 */
5172  if(ElementNumber > 1)
5173  {
5174  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
5175  {
5176  RouteOrPartRouteSet = true;
5177  }
5178  else
5179  {
5180  RouteOrPartRouteSet = false;
5181  }
5182  }
5183  if(!SkipRouteCheck && !RouteOrPartRouteSet)
5184  {
5185  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
5186  {
5187  Utilities->CallLogPop(1859);
5188  return(false);
5189  }
5190  int ExitLink = CurrentTrackElement.Link[ExitPos];
5191  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
5192  {
5193  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
5194  {
5195  Utilities->CallLogPop(1850);
5196  return(false);
5197  }
5198  }
5199  }
5200  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
5201  if(EntryPos < 2)
5202  {
5203  Distance += CurrentTrackElement.Length01;
5204  }
5205  else
5206  {
5207  Distance += CurrentTrackElement.Length23;
5208  }
5209  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
5210  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
5211  CurrentTrackVectorPosition = NextTrackVectorPosition;
5212  EntryPos = NextEntryPos;
5213  ElementNumber++;
5214  } // while(true)
5215 
5216  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
5217  // from the stop signal (note that it may be last in an autosigs route)
5218  // a single element route at the stop signal should have been removed prior to this function being called (that called before
5219  // this in ClockTimer2)
5220 
5221  // now add elements to the CallonVector
5222  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
5223 
5224  AllRoutes->CallonVector.push_back(CallonEntry);
5225  Utilities->CallLogPop(1860);
5226  return(true); // return false if fail to set route for any reason
5227 }
5228 
5229 // ---------------------------------------------------------------------------
5230 /*
5231  bool TTrain::TimetableFinished()
5232  {
5233  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
5234  {
5235  return true;
5236  }
5237  return false;
5238  }
5239 */
5240 // ---------------------------------------------------------------------------
5241 
5242 AnsiString TTrain::GetTrainHeadCode(int Caller)
5243 
5244 {
5245  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
5246  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
5247 
5248  Utilities->CallLogPop(1452);
5249  return(RepeatHeadCode);
5250 }
5251 
5252 // ---------------------------------------------------------------------------
5253 
5254 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
5255 {
5256  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
5257  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
5258 
5259  Utilities->CallLogPop(1453);
5260  return(RepeatTime);
5261 }
5262 
5263 // ---------------------------------------------------------------------------
5264 
5265 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
5266 {
5267  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
5268  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
5269  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
5270  // first check that train is fully on the railway
5271  bool FrontValid = false, RearValid = false;
5272  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
5273 
5274  if((LeadElement == -1) || (MidElement == -1))
5275  {
5276  TrainToBeJoinedBy = NULL;
5277  Utilities->CallLogPop(2131);
5278  return(false);
5279  }
5281  {
5282  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
5283  FrontValid = true;
5284  }
5286  {
5287  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
5288  RearValid = true;
5289  }
5290  int TrainToBeJoinedByID = -1;
5291 
5292  // first check if on a 2-track element & select correct ID number if so
5293  if(FrontValid)
5294  {
5295  if(FrontAdjacentTrackElement.TrackType == Bridge)
5296  {
5298  {
5299  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5300  }
5301  else
5302  {
5303  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5304  }
5305  }
5306  else
5307  {
5308  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
5309  }
5310  }
5311  if((TrainToBeJoinedByID < 0) && RearValid)
5312  {
5313  // first check if on a 2-track element & select correct ID number if so
5314  if(RearAdjacentTrackElement.TrackType == Bridge)
5315  {
5317  {
5318  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5319  }
5320  else
5321  {
5322  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5323  }
5324  }
5325  else
5326  {
5327  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5328  }
5329  }
5330  if(TrainToBeJoinedByID < 0) // no adjacent train
5331  {
5332  TrainToBeJoinedBy = NULL;
5333  Utilities->CallLogPop(2132);
5334  return(false);
5335  }
5336  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5337  if(!TrainToBeJoinedBy->Stopped())
5338  {
5339  TrainToBeJoinedBy = NULL;
5340  Utilities->CallLogPop(2133);
5341  return(false);
5342  }
5343  Utilities->CallLogPop(2134);
5344  return(true);
5345 }
5346 
5347 // ---------------------------------------------------------------------------
5348 
5349 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
5350  TDateTime TimetableNonRepeatTime, bool Warning)
5351 /*
5352  Time = timetable time, the time adjustments for repeat trains is carried out internally
5353  Not all messages need this, if not needed a dummy value is required but not used
5354 
5355  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5356  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5357  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5358  //NB for Frh just give terminated message but without event time - don't use this function
5359  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5360  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5361  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5362  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5363  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
5364  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
5365  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5366  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5367  ChangeDescription: 06:05:40: 2F46 changed its description to 'NewDescription' at Old Street 1 minute late
5368  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5369  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5370  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5371  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5372  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5373  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5374  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5375  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5376  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass stop signal
5377  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5378  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5379  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5380  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5381  SignallerStop 06:05:40: 2F46 stopped on signaller command
5382  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5383  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5384 */{
5385  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5386  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5387  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5388  int IntMinsLate = 0;
5389 
5390  // need to set it in case MinsLate == 0, since it isn't tested for that
5391  if(ActionType == Arrive)
5392  {
5393  ActionLog = " arrived at ";
5394  }
5395  if(ActionType == Terminate)
5396  {
5397  if(TerminatedMessageSent) // to avoid it being sent twice
5398  {
5399  Utilities->CallLogPop(1104);
5400  return;
5401  }
5402  ActionLog = " terminated at ";
5403  TerminatedMessageSent = true;
5404  }
5405  if(ActionType == Depart)
5406  {
5407  ActionLog = " departed from ";
5408  }
5409  if(ActionType == Pass)
5410  {
5411  ActionLog = " passed ";
5412  }
5413  if(ActionType == Create)
5414  {
5415  ActionLog = " created at ";
5416  }
5417  if(ActionType == Enter)
5418  {
5419  ActionLog = " entered railway at ";
5420  }
5421  if(ActionType == ChangeDescription)
5422  {
5423  ActionLog = " changed its description to '" + TrainDataEntryPtr->Description + "' at ";
5424  }
5425  if(ActionType == Leave)
5426  {
5427  ActionLog = " left railway at ";
5428  }
5429  if(ActionType == FrontSplit)
5430  {
5431  ActionLog = " split from front to ";
5432  }
5433  if(ActionType == RearSplit)
5434  {
5435  ActionLog = " split from rear to ";
5436  }
5437  if(ActionType == JoinedByOther)
5438  {
5439  ActionLog = " joined by ";
5440  }
5441  if(ActionType == ChangeDirection)
5442  {
5443  ActionLog = " changed direction at ";
5444  }
5445  if(ActionType == NewService)
5446  {
5447  ActionLog = " became new service ";
5448  }
5449  if(ActionType == TakeSignallerControl)
5450  {
5451  ActionLog = " taken under signaller control at ";
5452  }
5453  if(ActionType == RestoreTimetableControl)
5454  {
5455  ActionLog = " restored to timetable control at ";
5456  }
5457  if(ActionType == RemoveTrain)
5458  {
5459  if(Crashed)
5460  {
5461  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5462  }
5463  else if(Derailed)
5464  {
5465  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5466  }
5467  else
5468  {
5469  ActionLog = " REMOVED FROM RAILWAY at ";
5470  }
5471  }
5472  if(ActionType == SignallerMoveForwards)
5473  {
5474  ActionLog = " received signaller authority to proceed";
5475  }
5476  if(ActionType == SignallerStepForward)
5477  {
5478  ActionLog = " received signaller authority to step forward";
5479  }
5480  if(ActionType == SignallerChangeDirection)
5481  {
5482  ActionLog = " changed direction under signaller control at ";
5483  }
5484  if(ActionType == SignallerPassRedSignal)
5485  {
5486  ActionLog = " received signaller authority to pass stop signal";
5487  }
5488  if(ActionType == SignallerControlStop)
5489  {
5490  ActionLog = " received signaller instruction to stop";
5491  }
5492  if(ActionType == SignallerStop)
5493  {
5494  ActionLog = " stopped on signaller instruction ";
5495  }
5496  if(ActionType == SignallerJoin)
5497  {
5498  ActionLog = " joined under signaller control by ";
5499  }
5500  if(ActionType == TrainFailure)
5501  {
5502  ActionLog = " suffered an onboard power failure at ";
5503  }
5504  if(ActionType == RepairFailedTrain)
5505  {
5506  ActionLog = " failure repaired at ";
5507  }
5508  if(ActionType == SignallerLeave)
5509  {
5510  ActionLog = " left railway under signaller control at ";
5511  }
5512  if(OtherHeadCode != "")
5513  {
5514  OtherHeadCode += " at ";
5515  }
5516  TDateTime ActualTime = TrainController->TTClockTime;
5517 
5518  if(Warning)
5519  {
5520  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5521  WarningBaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName; //added time at v2.13.0
5522  }
5523  else
5524  {
5525  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5526  }
5527  bool TimePerformance = true;
5528 
5529  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5530  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5531  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5532  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5533  // SignallerJoin & RepairFailedTrain new at v2.4.0
5534  {
5535  TimePerformance = false;
5536  }
5537  if(TimePerformance)
5538  {
5539  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5540  MinsDelayed = float(MinsLate);
5541  if(ActionType == Pass) //added at v2.9.2 to prevent time to act increasing suddenly for early pass times then becoming 'NOW' when stops at signal
5542  {
5543  MinsDelayed = 0;
5544  }
5545  // new v2.2.0 for OpActionPanel, can be positive or negative
5546  if(ActionType == Arrive)
5547  {
5549  }
5550  // since train has just arrived this value is the
5551  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5552  // subtracted from later stop recoverable times.
5553  if(MinsLate < 0)
5554  {
5555  IntMinsLate = int(ceil(MinsLate));
5556  }
5557  if(MinsLate > 0)
5558  {
5559  IntMinsLate = int(floor(MinsLate));
5560  }
5561  if(IntMinsLate == 0)
5562  {
5563  PerfLog = " on time";
5564  }
5565  else if(IntMinsLate == 1)
5566  {
5567  PerfLog = " 1 minute late";
5568  }
5569  else if(IntMinsLate == -1)
5570  {
5571  PerfLog = " 1 minute early";
5572  }
5573  else if(IntMinsLate > 1)
5574  {
5575  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5576  }
5577  else if(IntMinsLate < -1)
5578  {
5579  int PosIntMinsLate = -IntMinsLate;
5580  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5581  }
5582  if(LocationName.Pos('-') > 0)
5583  {
5584  PerfLog = "," + PerfLog;
5585  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5586  }
5587  PerfLogForm->PerformanceLog(0, BaseLog + PerfLog);
5588  }
5589  else
5590  {
5591  PerfLogForm->PerformanceLog(1, BaseLog);
5592  }
5593  if(Warning)
5594  {
5595  Display->WarningLog(0, WarningBaseLog);
5596  }
5597  // update statistics
5598  if((ActionType == Arrive) && (IntMinsLate == 0))
5599  {
5601  }
5602  else if((ActionType == Arrive) && (IntMinsLate > 0))
5603  {
5605  TrainController->TotLateArrMins += IntMinsLate;
5606  }
5607  else if((ActionType == Arrive) && (IntMinsLate < 0))
5608  {
5610  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5611  }
5612 
5613  else if((ActionType == Pass) && (IntMinsLate == 0))
5614  {
5616  }
5617  else if((ActionType == Pass) && (IntMinsLate > 0))
5618  {
5620  TrainController->TotLatePassMins += IntMinsLate;
5621  }
5622  else if((ActionType == Pass) && (IntMinsLate < 0))
5623  {
5625  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5626  }
5627 
5628  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5629  {
5631  }
5632  else if((ActionType == Leave) && (IntMinsLate > 0))
5633  {
5635  TrainController->TotLateExitMins += IntMinsLate;
5636  }
5637  else if((ActionType == Leave) && (IntMinsLate < 0))
5638  {
5640  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5641  }
5642 
5643  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5644  {
5646  }
5647  else if((ActionType == Depart) && (IntMinsLate > 0))
5648  {
5650  TrainController->TotLateDepMins += IntMinsLate;
5651  }
5652  Utilities->CallLogPop(968);
5653 }
5654 
5655 // ---------------------------------------------------------------------------
5656 
5657 void TTrain::TrainHasFailed(int Caller)
5658 {
5659  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5660  if(Crashed || Derailed || DerailPending)
5661  {
5662  TrainFailurePending = false;
5663  Utilities->CallLogPop(2135);
5664  return;
5665  }
5666  AnsiString LocName = "";
5667 
5668  if(LeadElement > -1)
5669  {
5671  }
5672  if((LocName == "") && (MidElement > -1))
5673  {
5675  }
5676  if((LocName == "") && LeadElement > -1)
5677  {
5678  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5679  }
5680  if((LocName == "") && (MidElement > -1))
5681  {
5682  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5683  }
5684  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5685  TrainFailed = true;
5686  TrainFailurePending = false;
5687  CallingOnFlag = false; //added at v2.10.0
5689  PowerAtRail = 0.08;
5690  AValue = sqrt(2 * PowerAtRail / Mass);
5692  // TrainFailed only called when PlotElements properly set to Lead, Mid & Lag elements
5693  if(Stopped())
5694  {
5695  EntrySpeed = 0;
5696  ExitSpeedHalf = 0;
5697  ExitSpeedFull = 0;
5698  MaxExitSpeed = 0;
5699  BrakeRate = 0;
5700  StoppedWithoutPower = true;
5701  }
5703  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5704  // true for warning, TDateTime not used
5705  Utilities->CallLogPop(2136);
5706 }
5707 
5708 // ---------------------------------------------------------------------------
5709 
5710 void TTrain::FrontTrainSplit(int Caller)
5711 {
5712 /*
5713  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5714  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5715  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5716  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5717 */
5718  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5719  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5720  if(PowerAtRail < 1)
5721  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5722  {
5724  {
5725  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5726  }
5728  Utilities->CallLogPop(2137);
5729  return;
5730  }
5731  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5732 
5733  if(LocationName == "")
5734  {
5735  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5736  }
5737  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5738  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5739  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5741 
5742  // determine all positions & exits
5743  if(LocationName != "")
5744  {
5745  // if message given only call at ~5 sec intervals
5747  {
5748  FirstNamedElementPos = LeadElement;
5749  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5750  // check if possible with LeadElement as First
5751  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5752  {
5753  FirstNamedElementPos = MidElement;
5754  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5755  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5756  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5757  {
5759  {
5760  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5762  }
5763  Utilities->CallLogPop(1009);
5764  return;
5765  }
5766  }
5767  else
5768  {
5769  // if first is possible then check if all 4 positions at location, and if not try the second
5770  int LeadPosA = FirstNamedElementPos;
5771  int LeadPosB = FirstNamedLinkedElementPos;
5772  int LeadPosC = SecondNamedElementPos;
5773  int LeadPosD = SecondNamedLinkedElementPos;
5774  // count number of positions that are at the location
5775  int LeadNumAtLoc = 0;
5776  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5777  {
5778  LeadNumAtLoc++;
5779  }
5780  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5781  {
5782  LeadNumAtLoc++;
5783  }
5784  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5785  {
5786  LeadNumAtLoc++;
5787  }
5788  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5789  {
5790  LeadNumAtLoc++;
5791  }
5792  if(LeadNumAtLoc < 4)
5793  {
5794  FirstNamedElementPos = MidElement;
5795  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5796  SecondNamedLinkedElementPos)) // restore originals
5797  {
5798  FirstNamedElementPos = LeadPosA;
5799  FirstNamedLinkedElementPos = LeadPosB;
5800  SecondNamedElementPos = LeadPosC;
5801  SecondNamedLinkedElementPos = LeadPosD;
5802  }
5803  else // count number at location
5804  {
5805  int MidNumAtLoc = 0;
5806  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5807  {
5808  MidNumAtLoc++;
5809  }
5810  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5811  {
5812  MidNumAtLoc++;
5813  }
5814  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5815  {
5816  MidNumAtLoc++;
5817  }
5818  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5819  {
5820  MidNumAtLoc++;
5821  }
5822  if(LeadNumAtLoc > MidNumAtLoc)
5823  // change back, else keep new values
5824  {
5825  FirstNamedElementPos = LeadPosA;
5826  FirstNamedLinkedElementPos = LeadPosB;
5827  SecondNamedElementPos = LeadPosC;
5828  SecondNamedLinkedElementPos = LeadPosD;
5829  }
5830  }
5831  }
5832  }
5833  }
5834  else
5835  {
5836  Utilities->CallLogPop(1791);
5837  return;
5838  }
5839  }
5840  else
5841  {
5842  throw Exception("Error - LocationName not set in FrontTrainSplit");
5843  }
5844  // set front & rear train parameters
5845  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5846  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5847  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5848  if(LeadElement == FirstNamedElementPos)
5849  {
5850  if(MidElement == SecondNamedElementPos)
5851  {
5852  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5853  FrontTrainRearPosition = FirstNamedElementPos;
5854  RearTrainFrontPosition = SecondNamedElementPos;
5855  RearTrainRearPosition = SecondNamedLinkedElementPos;
5856  }
5857  else // MidElement must == FirstNamedLinkedElementPos
5858  {
5859  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5860  FrontTrainRearPosition = SecondNamedElementPos;
5861  RearTrainFrontPosition = FirstNamedElementPos;
5862  RearTrainRearPosition = FirstNamedLinkedElementPos;
5863  }
5864  }
5865  else // MidElement == FirstNamedElementPos
5866  {
5867  if(LeadElement == SecondNamedElementPos)
5868  {
5869  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5870  FrontTrainRearPosition = SecondNamedElementPos;
5871  RearTrainFrontPosition = FirstNamedElementPos;
5872  RearTrainRearPosition = FirstNamedLinkedElementPos;
5873  }
5874  else // LeadElement must == FirstNamedLinkedElementPos
5875  {
5876  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5877  FrontTrainRearPosition = FirstNamedElementPos;
5878  RearTrainFrontPosition = SecondNamedElementPos;
5879  RearTrainRearPosition = SecondNamedLinkedElementPos;
5880  }
5881  }
5882  RearTrainExitPos = -1;
5883  for(int x = 0; x < 4; x++)
5884  {
5885  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5886  {
5887  RearTrainExitPos = x;
5888  break;
5889  }
5890  }
5891  if(RearTrainExitPos == -1)
5892  {
5893  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5894  }
5895  FrontTrainExitPos = -1;
5896  for(int x = 0; x < 4; x++)
5897  {
5898  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5899  {
5900  FrontTrainExitPos = x;
5901  break;
5902  }
5903  }
5904  if(FrontTrainExitPos == -1)
5905  {
5906  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5907  }
5908  // check no train (apart from self) on any of the 4 elements & fail if so
5909  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5910  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5911 
5912  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5913  {
5914  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5915  }
5916  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5917  {
5918  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5919  }
5920  else
5921  {
5922  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5923  }
5924  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5925  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5926  // can't be a bridge
5927  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5928  // can't be a bridge
5929  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5930  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5931 
5932  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5933  {
5934  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5935  }
5936  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5937  {
5938  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5939  }
5940  else
5941  {
5942  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5943  }
5944  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5945  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5946  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5947  {
5949  {
5952  }
5953  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5954  Utilities->CallLogPop(1010);
5955  return;
5956  }
5958  {
5960  }
5961  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5962  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5963  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5964  // variable as it is needed for setting up the new train
5965  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5966  AnsiString OriginalDescription = TrainDataEntryPtr->Description; //new at v2.15.0 to record earlier service description
5967 
5968  UnplotTrain(0);
5969  StartSpeed = 0;
5970  RearStartElement = RearTrainRearPosition;
5971  RearStartExitPos = RearTrainExitPos;
5972  StoppedAtLocation = true;
5973  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5974  {
5975  StoppedWithoutPower = true;
5976  }
5977  PlotStartPosition(3);
5980 // ActionVectorEntryPtr++; moved lower down at v2.15.0 because of new section below
5982 
5983  //new at v2.15.0 for unequal split in mass & power
5984  int NewTrainMass;
5985  double NewTrainPowerAtRail;
5987  {
5988  int pos = ActionVectorEntryPtr->SplitDistribution.Pos('-');
5989  int MassPercent = ActionVectorEntryPtr->SplitDistribution.SubString(1, pos - 1).ToInt(); //validity checked during validation
5990  int PowerPercent = ActionVectorEntryPtr->SplitDistribution.SubString(pos + 1, ActionVectorEntryPtr->SplitDistribution.Length() - pos).ToInt();
5991  NewTrainMass = Mass * double(MassPercent)/100.0;
5992  Mass = Mass - NewTrainMass;
5993  NewTrainPowerAtRail = PowerAtRail * double(PowerPercent)/100.0;
5994  if(NewTrainPowerAtRail == 0)
5995  {
5996  NewTrainPowerAtRail = 0.08; //min value represents 0
5997  }
5998  PowerAtRail = PowerAtRail - NewTrainPowerAtRail;
5999  AValue = sqrt(2 * PowerAtRail / Mass);
6000  }
6001  else
6002  {
6003  Mass = Mass / 2;
6004  NewTrainMass = Mass;
6005  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6006  // MaxBrakeRate = MaxBrakeRate / 2; this was wrong - want brake rate to stay the same, brake force is halved but that not a train parameter
6007  // and when needed it's calculated from rate & mass - changed at v2.15.0
6008  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6009  PowerAtRail = PowerAtRail / 2;
6010  NewTrainPowerAtRail = PowerAtRail;
6011  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6012  AValue = sqrt(2 * PowerAtRail / Mass);
6013  // shouldn't change but include in case not set earlier
6014  }
6015 
6016  ActionVectorEntryPtr++; //moved here at v2.15.0
6017  // create new front train
6018 /*
6019  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
6020  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
6021  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
6022 */
6023  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6024  TActionEventType EventType = NoEvent;
6025 
6026  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, NewTrainMass, MaxRunningSpeed, MaxBrakeRate, NewTrainPowerAtRail,
6027  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6028  // false for SignallerControl
6029  {
6030  Utilities->CallLogPop(1721); // EventType not used here
6031  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6032  // another train, in which case a message will have been sent to the perf log, also might well clear later
6033  // when other train moves away
6034  return;
6035  }
6036 
6037  if(!OldActionVectorEntryPtr->LinkedTrainEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6038  {
6039  OldActionVectorEntryPtr->LinkedTrainEntryPtr->Description = OriginalDescription;
6040  }
6041 
6042  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6043  // see mods in UpdateTrain for v1.3.2
6044  TrainController->TrainAdded = true;
6045 
6046  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6047 
6048  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6049  TTOD.RunningEntry = Running;
6050  Utilities->CallLogPop(998);
6051 }
6052 
6053 // ---------------------------------------------------------------------------
6054 
6055 void TTrain::RearTrainSplit(int Caller)
6056 {
6057 /*
6058  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
6059  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
6060  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
6061  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
6062 */
6063  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
6064  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
6065  if(PowerAtRail < 1)
6066  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
6067  {
6069  {
6070  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
6071  }
6073  Utilities->CallLogPop(2138);
6074  return;
6075  }
6076  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
6077 
6078  if(LocationName == "")
6079  {
6080  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
6081  }
6082  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
6083  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
6084  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
6086 
6087  // determine all positions & exits
6088  if(LocationName != "")
6089  {
6090  // if message given only call at ~5 sec intervals
6092  {
6093  FirstNamedElementPos = LeadElement;
6094  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6095  SecondNamedLinkedElementPos))
6096  {
6097  FirstNamedElementPos = MidElement;
6098  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6099  SecondNamedLinkedElementPos))
6100  {
6102  {
6103  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
6105  }
6106  Utilities->CallLogPop(1013);
6107  return;
6108  }
6109  }
6110  else
6111  {
6112  // if first is possible then check if all 4 positions at location, and if not try the second
6113  int LeadPosA = FirstNamedElementPos;
6114  int LeadPosB = FirstNamedLinkedElementPos;
6115  int LeadPosC = SecondNamedElementPos;
6116  int LeadPosD = SecondNamedLinkedElementPos;
6117  // count number of positions that are at the location
6118  int LeadNumAtLoc = 0;
6119  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
6120  {
6121  LeadNumAtLoc++;
6122  }
6123  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
6124  {
6125  LeadNumAtLoc++;
6126  }
6127  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
6128  {
6129  LeadNumAtLoc++;
6130  }
6131  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
6132  {
6133  LeadNumAtLoc++;
6134  }
6135  if(LeadNumAtLoc < 4)
6136  {
6137  FirstNamedElementPos = MidElement;
6138  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6139  SecondNamedLinkedElementPos)) // restore originals
6140  {
6141  FirstNamedElementPos = LeadPosA;
6142  FirstNamedLinkedElementPos = LeadPosB;
6143  SecondNamedElementPos = LeadPosC;
6144  SecondNamedLinkedElementPos = LeadPosD;
6145  }
6146  else // count number at location
6147  {
6148  int MidNumAtLoc = 0;
6149  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
6150  {
6151  MidNumAtLoc++;
6152  }
6153  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6154  {
6155  MidNumAtLoc++;
6156  }
6157  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6158  {
6159  MidNumAtLoc++;
6160  }
6161  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6162  {
6163  MidNumAtLoc++;
6164  }
6165  if(LeadNumAtLoc > MidNumAtLoc)
6166  // change back, else keep new values
6167  {
6168  FirstNamedElementPos = LeadPosA;
6169  FirstNamedLinkedElementPos = LeadPosB;
6170  SecondNamedElementPos = LeadPosC;
6171  SecondNamedLinkedElementPos = LeadPosD;
6172  }
6173  }
6174  }
6175  }
6176  }
6177  else
6178  {
6179  Utilities->CallLogPop(1792);
6180  return;
6181  }
6182  }
6183  else
6184  {
6185  throw Exception("Error - LocationName not set in RearTrainSplit");
6186  }
6187  // set front & rear train parameters
6188  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
6189  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
6190  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
6191  if(LeadElement == FirstNamedElementPos)
6192  {
6193  if(MidElement == SecondNamedElementPos)
6194  {
6195  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
6196  FrontTrainRearPosition = FirstNamedElementPos;
6197  RearTrainFrontPosition = SecondNamedElementPos;
6198  RearTrainRearPosition = SecondNamedLinkedElementPos;
6199  }
6200  else // MidElement must == FirstNamedLinkedElementPos
6201  {
6202  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
6203  FrontTrainRearPosition = SecondNamedElementPos;
6204  RearTrainFrontPosition = FirstNamedElementPos;
6205  RearTrainRearPosition = FirstNamedLinkedElementPos;
6206  }
6207  }
6208  else // MidElement == FirstNamedElementPos
6209  {
6210  if(LeadElement == SecondNamedElementPos)
6211  {
6212  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
6213  FrontTrainRearPosition = SecondNamedElementPos;
6214  RearTrainFrontPosition = FirstNamedElementPos;
6215  RearTrainRearPosition = FirstNamedLinkedElementPos;
6216  }
6217  else // LeadElement must == FirstNamedLinkedElementPos
6218  {
6219  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
6220  FrontTrainRearPosition = FirstNamedElementPos;
6221  RearTrainFrontPosition = SecondNamedElementPos;
6222  RearTrainRearPosition = SecondNamedLinkedElementPos;
6223  }
6224  }
6225  RearTrainExitPos = -1;
6226  for(int x = 0; x < 4; x++)
6227  {
6228  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
6229  {
6230  RearTrainExitPos = x;
6231  break;
6232  }
6233  }
6234  if(RearTrainExitPos == -1)
6235  {
6236  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
6237  }
6238  FrontTrainExitPos = -1;
6239  for(int x = 0; x < 4; x++)
6240  {
6241  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
6242  {
6243  FrontTrainExitPos = x;
6244  break;
6245  }
6246  }
6247  if(FrontTrainExitPos == -1)
6248  {
6249  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
6250  }
6251  // check no train (apart from self) on any of the 4 elements & fail if so
6252  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
6253  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
6254 
6255  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
6256  {
6257  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
6258  }
6259  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
6260  {
6261  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
6262  }
6263  else
6264  {
6265  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
6266  }
6267  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
6268  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
6269  // can't be a bridge
6270  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
6271  // can't be a bridge
6272  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
6273  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
6274 
6275  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
6276  {
6277  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
6278  }
6279  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
6280  {
6281  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
6282  }
6283  else
6284  {
6285  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
6286  }
6287  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
6288  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
6289  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
6290  {
6292  {
6295  }
6296  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
6297  Utilities->CallLogPop(1014);
6298  return;
6299  }
6301  {
6303  }
6304  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
6305  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
6306  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
6307  // variable as it is needed for setting up the new train
6308  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
6309  AnsiString OriginalDescription = TrainDataEntryPtr->Description; //new at v2.15.0 to record earlier service description
6310  UnplotTrain(1);
6311  StartSpeed = 0;
6312  RearStartElement = FrontTrainRearPosition;
6313  RearStartExitPos = FrontTrainExitPos;
6314  StoppedAtLocation = true;
6315  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
6316  {
6317  StoppedWithoutPower = true;
6318  }
6319  PlotStartPosition(4);
6322 // ActionVectorEntryPtr++; moved lower down at v2.15.0 because of new section below
6324 
6325  //new at v2.15.0 for unequal split in mass & power
6326  int NewTrainMass;
6327  double NewTrainPowerAtRail;
6329  {
6330  int pos = ActionVectorEntryPtr->SplitDistribution.Pos('-');
6331  int MassPercent = ActionVectorEntryPtr->SplitDistribution.SubString(1, pos - 1).ToInt(); //validity checked during validation
6332  int PowerPercent = ActionVectorEntryPtr->SplitDistribution.SubString(pos + 1, ActionVectorEntryPtr->SplitDistribution.Length() - pos).ToInt();
6333  NewTrainMass = Mass * double(MassPercent)/100.0;
6334  Mass = Mass - NewTrainMass;
6335  NewTrainPowerAtRail = PowerAtRail * double(PowerPercent)/100.0;
6336  if(NewTrainPowerAtRail == 0)
6337  {
6338  NewTrainPowerAtRail = 0.08; //min value represents 0
6339  }
6340  PowerAtRail = PowerAtRail - NewTrainPowerAtRail ;
6341  AValue = sqrt(2 * PowerAtRail / Mass);
6342  }
6343  else
6344  {
6345  Mass = Mass / 2;
6346  NewTrainMass = Mass;
6347  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6348  // MaxBrakeRate = MaxBrakeRate / 2; this was wrong - want brake rate to stay the same, brake force is halved but that not a train parameter
6349  // and when needed it's calculated from rate & mass - changed at v2.15.0
6350  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6351  PowerAtRail = PowerAtRail / 2;
6352  NewTrainPowerAtRail = PowerAtRail;
6353  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6354  AValue = sqrt(2 * PowerAtRail / Mass);
6355  // shouldn't change but include in case not set earlier
6356  }
6357 
6358  ActionVectorEntryPtr++; //moved here at v2.15.0
6359 
6360  // create new rear train
6361 /*
6362  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
6363  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
6364  int RepeatNumber, int IncrementalMinutes)
6365 */
6366  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6367  TActionEventType EventType = NoEvent;
6368 
6369  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, NewTrainMass, MaxRunningSpeed, MaxBrakeRate, NewTrainPowerAtRail,
6370  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6371  // false for SignallerControl
6372  {
6373  Utilities->CallLogPop(1722); // EventType not used here
6374  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6375  // another train, in which case a message will have been sent to the perf log, also might well clear later
6376  // when other train moves away
6377  return;
6378  }
6379 
6380  if(!OldActionVectorEntryPtr->LinkedTrainEntryPtr->ExplicitDescription) //new at v2.15.0 see above (after addtrain & return)
6381  {
6382  OldActionVectorEntryPtr->LinkedTrainEntryPtr->Description = OriginalDescription;
6383  }
6384  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6385  // see mods in UpdateTrain for v1.3.2
6386  TrainController->TrainAdded = true;
6387  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6388 
6389  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6390  TTOD.RunningEntry = Running;
6391  Utilities->CallLogPop(1015);
6392 }
6393 
6394 // ---------------------------------------------------------------------------
6395 
6396 void TTrain::FinishJoin(int Caller)
6397 {
6398  if(FinishJoinLogSent == false)
6399  {
6400  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6401  FinishJoinLogSent = true; // so don't keep logging this event
6402  // don't need to reset it to false after the event as the train is deleted
6403  }
6404  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6405  if(TrainFailed)
6406  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6407  {
6409  {
6410  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6411  }
6413  Utilities->CallLogPop(2139);
6414  return;
6415  }
6416  if(TrainGone)
6417  // this means that the train has already joined the other & is awaiting deletion by TrainController
6418  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6419  // on from jbo & TrainToJoinIsAdjacent returns false
6420  {
6421  Utilities->CallLogPop(1035);
6422  return;
6423  }
6424  TTrain *TrainToJoin;
6426 
6427  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6428  {
6430  {
6431  // PerfLogForm->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6434  }
6435  Utilities->CallLogPop(1030);
6436  return; // keep this here in case need to add code before final return
6437  }
6438  // no need to clear error report flag here, cleared in jbo function
6439  // No need to set TimetableFinished, done in jbo function
6440  Utilities->CallLogPop(1031);
6441 }
6442 
6443 // ---------------------------------------------------------------------------
6444 
6445 void TTrain::JoinedBy(int Caller)
6446 {
6447  if(TrainController->OpTimeToActUpdateCounter == 0) //added at v2.13.2. Use OpTimeToActUpdateCounter for convenience so only issue the event log
6448  //once every second rather than many times. Can't use an event logged flag because there may
6449  //be several trains that are to be joined by others
6450  {
6451  TrainController->LogEvent("" + AnsiString(Caller) + "," + HeadCode + ",Waiting to be joined");
6452  }
6453  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6454  if(PowerAtRail < 1)
6455  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6456  {
6458  {
6459  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6460  }
6462  Utilities->CallLogPop(2140);
6463  return;
6464  }
6465  TTrain *TrainToBeJoinedBy;
6467 
6468  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6469  {
6471  {
6472  // PerfLogForm->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6475  }
6476  LastActionDelayFlag = true;
6477  // need to update LastActionTime if this train first to arrive as need 30s after
6478  // both adjacent before the join
6479  Utilities->CallLogPop(1032);
6480  return;
6481  }
6482  // here when other train is adjacent
6484  {
6486  // need to update this as need 30s after both adjacent before the join
6487  LastActionDelayFlag = false;
6488  Utilities->CallLogPop(1033);
6489  return;
6490  }
6491  // here when other train is adjacent & 30 secs elapsed since both adjacent
6492 
6493  // set new values for mass etc
6494  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6495  {
6496  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6497  }
6498  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6499  double OwnBrakeForce = MaxBrakeRate * Mass;
6500  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6501 
6502  Mass += TrainToBeJoinedBy->Mass;
6503  MaxBrakeRate = CombinedBrakeRate;
6504  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6505  AValue = sqrt(2 * PowerAtRail / Mass);
6506 
6508  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6509  TrainToBeJoinedBy->TimetableFinished = true;
6510  TrainToBeJoinedBy->TrainGone = true;
6511  TrainController->LogEvent("" + AnsiString(Caller) + "," + HeadCode + ",Joined By," + FJOHeadCode); //added at v2.13.2 to provide more information
6512  // this will cause other train to be deleted
6513  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6517  Utilities->CallLogPop(1034);
6518 }
6519 
6520 // ---------------------------------------------------------------------------
6521 
6522 void TTrain::ChangeTrainDirection(int Caller, bool NoLogFlag)
6523 {
6524  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6525  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6526  if(PowerAtRail < 1)
6527  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6528  {
6530  {
6531  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6532  }
6533  ZeroPowerNoCDTMessage = true;
6534  Utilities->CallLogPop(2141);
6535  return;
6536  }
6537  TColor TempColour = BackgroundColour;
6538 
6539  UnplotTrain(2);
6542  StartSpeed = 0;
6543  StoppedAtLocation = true;
6544  PlotStartPosition(1);
6545  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6546  // plot same as was - should always be pale green
6547  if(!NoLogFlag)
6548  {
6551  }
6553 
6554  //now erase a stub route if there is one, added at v2.5.1
6555  //first element of route is now immediately behind the train (i.e. next to MidElement)
6556  if(MidEntryPos >= 0)
6557  {
6558  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6559  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6560  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6561  int RouteNumber = -1;
6562  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6563  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6564  {
6565  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6566  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
6567  //elements can continue to be removed from that route
6568  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6569  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6570  {
6571  bool FirstPass = true; //added at v2.8.0
6572  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6573  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
6574  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6575  int TVPos2 = PDE.GetTrackVectorPosition();
6576  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6577  {
6578  break;
6579  }
6580  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6582  {
6583  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6584  }
6585  else
6586  {
6587  break;
6588  }
6589  FirstPass = false;
6590  }
6591  AllRoutes->RebuildRailwayFlag = true;
6592  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6593  }
6594  }
6595  }
6596  StoppedForTrainInFront = false;
6597  Utilities->CallLogPop(1012);
6598 }
6599 
6600 // ---------------------------------------------------------------------------
6601 
6602 void TTrain::NewTrainService(int Caller, bool NoLogFlag) //, bool NoLogFlag added at v2.12.0 for new service tt skips
6603 // change to new train, give new service message
6604 //same RepeatNumber used for the new service
6605 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6606  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6607  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6608  if(PowerAtRail < 1)
6609  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6610  {
6612  {
6613  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6614  }
6616  Utilities->CallLogPop(2142);
6617  return;
6618  }
6620 
6621  AnsiString OldDescription = TrainDataEntryPtr->Description; //new at v2.15.0 to record earlier service description
6622 
6623  if(!NoLogFlag)
6624  {
6626  }
6627  UnplotTrain(3);
6630  StartSpeed = 0;
6635  HeadCode = NewHeadCode;
6636  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6637  {
6638  TrainDataEntryPtr->Description = OldDescription;
6639  }
6640  StoppedAtLocation = true;
6641  PlotStartPosition(5);
6643  // pale green
6646  TerminatedMessageSent = false;
6647  Utilities->CallLogPop(1022);
6648 }
6649 
6650 // ---------------------------------------------------------------------------
6651 
6652 void TTrain::RemainHere(int Caller)
6653 {
6654  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6655  if(RemainHereLogNotSent) // to prevent repeated logs
6656  {
6657  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6658  RemainHereLogNotSent = false;
6659  }
6661  {
6665  TerminatedMessageSent = true;
6666  }
6667  TimetableFinished = true;
6668  Utilities->CallLogPop(1023);
6669 }
6670 
6671 // ---------------------------------------------------------------------------
6672 
6673 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6674 /*
6675  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6676  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6677  except where an action is a departure, starting at the current value for the pointer
6678  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6679  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6680 */{
6681  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6682  {
6683  return; // if remove train that starts under signaller control no messages needed
6684 
6685  }
6686  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6687  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6688  if(IncNum > 0)
6689  {
6690  for(int x = 0; x < IncNum; x++)
6691  {
6692  if(x > 0)
6693  {
6694  Ptr++;
6695  }
6696  // arrival - no need to test for termination as this section only covers missed actions up to the
6697  // arrival point - may terminate later but that not missed
6698  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6699  {
6701  }
6702  // arrival & departure
6703  if(Ptr->FormatType == TimeTimeLoc)
6704  {
6706  }
6707  // departure
6708  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6709  {
6710  continue; // skip TimeLoc departures, message given for arrivals
6711  }
6712  // pass
6713  else if(Ptr->FormatType == PassTime)
6714  {
6716  }
6717  // split
6718  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6719  {
6721  }
6722  // jbo
6723  else if(Ptr->Command == "jbo")
6724  {
6726  }
6727  // dsc
6728  else if(Ptr->Command == "dsc") //new at v2.15.0
6729  {
6731  }
6732  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6733  // be starts, finishes or cdt
6734  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6735  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6736  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6737  (Ptr->FormatType == Repeat))
6738  {
6739  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6740  }
6741  }
6742  }
6743  else
6744  {
6745  bool IncludeFER = false;
6746  if(IncNum == -1)
6747  {
6748  IncludeFER = true;
6749  }
6750  while(true) // finish commands & repeats break out of loop
6751  {
6752  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6753  if(!IncludeFER && (Ptr->Command == "Fer"))
6754  {
6755  break;
6756  }
6757  // Fer & included
6758  else if(IncludeFER && (Ptr->Command == "Fer"))
6759  {
6761  break;
6762  }
6763  // Repeat
6764  else if(Ptr->FormatType == Repeat)
6765  {
6766  break;
6767  }
6768  // Fjo
6769  else if(Ptr->Command == "Fjo")
6770  {
6772  break;
6773  }
6774  // Frh
6775  else if(Ptr->Command == "Frh")
6776  {
6778  {
6780  TerminatedMessageSent = true;
6781  }
6782  break;
6783  }
6784  // Frh-sh
6785  else if(Ptr->Command == "Frh-sh")
6786  {
6788  {
6790  TerminatedMessageSent = true;
6791  }
6792  break;
6793  }
6794  // Fns, F-nshs, Fns-sh
6795  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6796  {
6798  break;
6799  }
6800  // end of breakout actions
6801  // arrival
6802  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6803  {
6804  if(IsTrainTerminating(1))
6805  {
6807  TerminatedMessageSent = true;
6808  }
6809  else
6810  {
6812  }
6813  }
6814  // arrival & departure
6815  else if(Ptr->FormatType == TimeTimeLoc)
6816  {
6818  }
6819  // departure
6820  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6821  {
6822  Ptr++;
6823  continue; // skip TimeLoc departures, message given for arrivals
6824  }
6825  // pass
6826  else if(Ptr->FormatType == PassTime)
6827  {
6829  }
6830  // split
6831  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6832  {
6834  }
6835  // jbo
6836  else if(Ptr->Command == "jbo")
6837  {
6839  }
6840  // dsc
6841  else if(Ptr->Command == "dsc") //new at v2.15.0
6842  {
6843 // TrainController->LogActionError(65, HeadCode, "", FailMissedDSC, Ptr->LocationName); don't count as a missed event
6844  }
6845  // cdt
6846  else if(Ptr->Command == "cdt")
6847  {
6848 // TrainController->LogActionError(25, HeadCode, "", FailMissedChangeDirection, Ptr->LocationName); //commented out at v2.12.0 as cdts not counted as missed events
6849  }
6850  // Errors
6851  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6852  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6853  {
6854  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6855  }
6856  Ptr++;
6857  }
6858  TimetableFinished = true;
6859  }
6860  Utilities->CallLogPop(1021);
6861 }
6862 
6863 // ---------------------------------------------------------------------------
6864 
6865 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6866 // ensure same repeatnumber
6867 {
6868  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6870 
6871  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6872  {
6873  Utilities->CallLogPop(1024);
6874  return(false);
6875  }
6876  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6877  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6878  {
6879  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6880  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6881  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6882  {
6883  Utilities->CallLogPop(1025);
6884  return(true);
6885  }
6886  }
6887  Utilities->CallLogPop(1026);
6888  return(false);
6889 }
6890 
6891 // ---------------------------------------------------------------------------
6892 
6893 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6894 // ensure same repeatnumber
6895 {
6896  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6897  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6898 
6899  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6900  {
6901  Utilities->CallLogPop(1027);
6902  return(false);
6903  }
6904  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6905  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6906  {
6907  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6908  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6909  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6910  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6911  {
6912  Utilities->CallLogPop(1028);
6913  return(true);
6914  }
6915  }
6916  Utilities->CallLogPop(1029);
6917  return(false);
6918 }
6919 
6920 // ---------------------------------------------------------------------------
6921 
6922 void TTrain::NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6923 { //same RepeatNumber (i.e. 0) used for the new shuttle
6924 //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6925  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6926  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6927  if(PowerAtRail < 1)
6928  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6929  {
6931  {
6932  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6933  }
6935  Utilities->CallLogPop(2143);
6936  return;
6937  }
6938  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6939  AnsiString OldDescription = TrainDataEntryPtr->Description; //new at v2.15.0 to record earlier service description
6940 
6941  if(!NoLogFlag)
6942  {
6944  }
6945  UnplotTrain(4);
6948  StartSpeed = 0;
6953  HeadCode = NewHeadCode;
6954  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6955  {
6956  TrainDataEntryPtr->Description = OldDescription;
6957  }
6958  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6959  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6960  StoppedAtLocation = true;
6961  PlotStartPosition(6);
6963  // pale green
6966  TerminatedMessageSent = false;
6967  Utilities->CallLogPop(1078);
6968 }
6969 
6970 // ---------------------------------------------------------------------------
6971 
6972 void TTrain::RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6973 // need to check whether all repeats finished or not
6974 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6975  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6976  if(RemainHereLogNotSent) // to prevent repeated logs
6977  {
6978  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6979  RemainHereLogNotSent = false;
6980  }
6982  // finished all repeats
6983  {
6985  { //no need to suppress this LogAction because BecomeNewService won't be available in this case
6988  TerminatedMessageSent = true;
6989  // no need to clear message as no more actions
6990  }
6991  TimetableFinished = true;
6992  Utilities->CallLogPop(1080);
6993  return;
6994  }
6995  if(PowerAtRail < 1)
6996  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6997  {
6999  {
7000  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
7001  }
7003  Utilities->CallLogPop(2144);
7004  return;
7005  }
7006  int TempRepeatNumber = RepeatNumber + 1;
7007  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
7008  // until after LogAction or the wrong time will be used
7009  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
7010 
7011  if(!NoLogFlag)
7012  {
7014  }
7015  RepeatNumber++;
7016  UnplotTrain(5);
7019  StartSpeed = 0;
7024  HeadCode = NewHeadCode;
7025  StoppedAtLocation = true;
7026  PlotStartPosition(7);
7028  // pale green
7031  TerminatedMessageSent = false;
7032  Utilities->CallLogPop(1079);
7033 }
7034 
7035 // ---------------------------------------------------------------------------
7036 
7037 void TTrain::RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips
7038 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
7039  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
7040  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
7041  if(PowerAtRail < 1)
7042  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
7043  {
7045  {
7046  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
7047  }
7049  Utilities->CallLogPop(2145);
7050  return;
7051  }
7053  // finished all repeats
7054  {
7055  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
7056  if(!NoLogFlag)
7057  {
7059  }
7060  RepeatNumber = 0;
7061  AnsiString OldDescription = TrainDataEntryPtr->Description; //new at v2.15.0 to record earlier service description
7062  UnplotTrain(6);
7065  StartSpeed = 0;
7067  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
7068  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
7070  HeadCode = NewHeadCode;
7071  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
7072  {
7073  TrainDataEntryPtr->Description = OldDescription;
7074  }
7075  StoppedAtLocation = true;
7076  PlotStartPosition(9);
7080  TerminatedMessageSent = false;
7081  Utilities->CallLogPop(1081);
7082  return;
7083  }
7084  int TempRepeatNumber = RepeatNumber + 1;
7085  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
7086  // until after LogAction or the wrong time will be used
7087  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
7088 
7089  if(!NoLogFlag)
7090  {
7092  }
7093  RepeatNumber++;
7094  UnplotTrain(7);
7097  StartSpeed = 0;
7102  HeadCode = NewHeadCode;
7103  StoppedAtLocation = true;
7104  PlotStartPosition(8);
7106  // pale green
7109  TerminatedMessageSent = false;
7110  Utilities->CallLogPop(1082);
7111 }
7112 
7113 // ---------------------------------------------------------------------------
7114 
7116 {
7117  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
7118  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
7119  // must be preceded by a TimeLoc departure
7120  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
7121  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
7122  {
7124  {
7125  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
7126  {
7127  Utilities->CallLogPop(1083);
7128  return(false);
7129  }
7130  else if((ActionVectorEntryPtr + x)->SequenceType == FinishSequence)
7131  {
7132  Utilities->CallLogPop(1084);
7133  return(true);
7134  }
7135  }
7136  }
7137  Utilities->CallLogPop(1085);
7138  return(false);
7139 }
7140 
7141 // ---------------------------------------------------------------------------
7142 
7143 bool TTrain::AbleToMove(int Caller)
7144 {
7145  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
7146  bool Able = true;
7147 
7148  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0
7149  {
7150  // StoppedForTrainInFront removed as tested below
7151  Utilities->CallLogPop(2146); // added v2.4.0
7152  return(false); // added v2.4.0
7153  }
7154  if(LeadElement > -1)
7155  {
7156  if(Track->TrackElementAt(801, LeadElement).TrackType == Buffers) //moved up here from 'else' below at v2.12.0
7157  {
7158  StoppedForTrainInFront = false;
7159  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
7160  Utilities->CallLogPop(2456);
7161  return(false);
7162  }
7163  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
7164  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
7165  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront) //check if train in front still there
7166  {
7167  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
7168  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
7169  {
7170  Able = true;
7171  StoppedForTrainInFront = false;
7172  }
7173  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 == -1))
7174  {
7175  Able = true;
7176  StoppedForTrainInFront = false;
7177  }
7178  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 == -1))
7179  {
7180  Able = true;
7181  StoppedForTrainInFront = false;
7182  }
7183  else
7184  {
7185  Able = false; //added at v2.12.0 as train still in front so don't want signaller popup options to move available
7186  }
7187  }
7188  }
7189  else // leaving at a continuation so keep going
7190  {
7191  Able = true;
7192  StoppedForTrainInFront = false;
7193  }
7194  Utilities->CallLogPop(1454);
7195  return(Able);
7196 }
7197 
7198 // ---------------------------------------------------------------------------
7199 
7201 {
7202  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
7203  // won't be set; if there is a train then set StoppedForTrainInFront
7204  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
7205  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
7206  if(LeadElement == -1) // exiting at continuation
7207  {
7208  Utilities->CallLogPop(2045);
7209  return(false);
7210  }
7211  // end of addition
7212  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
7213  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
7214 
7215  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
7216  {
7217  StoppedForTrainInFront = true;
7218  Utilities->CallLogPop(1455);
7219  return(false);
7220  }
7221  else
7222  {
7223  Utilities->CallLogPop(1456);
7225  // StoppedWithoutPower added v2.4.0
7226  }
7227 }
7228 
7229 // ---------------------------------------------------------------------------
7230 
7232 {
7233  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
7234  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
7235  TColor TempColour = BackgroundColour;
7236 
7237  UnplotTrain(8);
7240  StartSpeed = 0;
7241  PlotStartPosition(2);
7242  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
7243 
7244  //now erase a stub route if there is one, added at v2.5.1
7245  //first element of route is now immediately behind the train (i.e. next to MidElement)
7246  if(MidEntryPos >= 0)
7247  {
7248  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
7249  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
7250  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
7251  int RouteNumber = -1;
7252  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
7253  if(RouteType == TAllRoutes::NotAutoSigsRoute)
7254  {
7255  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
7256  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
7257  //elements can continue to be removed from that route
7258  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
7259  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
7260  {
7261  bool FirstPass = true; //added at v2.8.0
7262  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
7263  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
7264  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
7265  int TVPos2 = PDE.GetTrackVectorPosition();
7266  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
7267  {
7268  break;
7269  }
7270  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
7272  {
7273  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
7274  }
7275  else
7276  {
7277  break;
7278  }
7279  FirstPass = false;
7280  }
7281  AllRoutes->RebuildRailwayFlag = true;
7282  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
7283  }
7284  }
7285  }
7286  Utilities->CallLogPop(1102);
7287 }
7288 
7289 // ---------------------------------------------------------------------------
7290 
7292 {
7293  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7294  ",FloatingLabelNextString" + "," + HeadCode);
7295  AnsiString RetStr = "", LocationName = "";
7296  //record action time - may be arrival, departure or event for use later (added at v2.13.2)
7297  TDateTime ActionTime = Ptr->ArrivalTime;
7298  if(ActionTime == TDateTime(-1))
7299  {
7300  ActionTime = Ptr->DepartureTime;
7301  }
7302  if(ActionTime == TDateTime(-1))
7303  {
7304  ActionTime = Ptr->EventTime;
7305  }
7306  //If ActionTime still TDateTime(-1) then the train has terminated and 'None...' will be returned
7307  //Now correct it for repeats
7308  if(ActionTime != TDateTime(-1))
7309  {
7310  ActionTime = GetTrainTime(64, ActionTime);
7311  }
7312  if(int(DelayedRandMins) > 0)
7313  {
7314  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7315  {
7316  throw Exception("Error - start entry in FloatingLabelNextString");
7317  }
7318  if(Ptr->FormatType == TimeTimeLoc)
7319  {
7320  if(TrainMode == Timetable)
7321  {
7322  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
7323  // not arrived yet in tt mode
7324  {
7325  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7326  }
7327  else
7328  {
7329  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7330  }
7331  }
7332  else // TrainMode == Signaller
7333  {
7334  if(!DepartureTimeSet) // not arrived yet
7335  {
7336  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7337  }
7338  else
7339  {
7340  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7341  }
7342  }
7343  }
7344  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7345  {
7346  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7347  }
7348  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7349  {
7350  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7351  }
7352  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7353  {
7354  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(46, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7355  }
7356  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7357  {
7358  RetStr = "Pass " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7359  }
7360  else if(Ptr->Command == "Fns")
7361  {
7362  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7363  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7364  RetStr = GetNewServiceDepartureInfo(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7365  }
7366  else if(Ptr->Command == "F-nshs")
7367  {
7368  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7369  Utilities->Format96HHMM(GetTrainTime(32, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7370  RetStr = GetNewServiceDepartureInfo(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7371  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7372  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7373  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7374  }
7375  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7376  {
7377  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7378  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7379  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7380  RetStr = GetNewServiceDepartureInfo(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7381  }
7382  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7383  {
7384  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7385  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7386  RetStr = GetNewServiceDepartureInfo(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7387  }
7388  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7389  {
7390  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7391  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7392  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7393  RetStr = GetNewServiceDepartureInfo(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7394  }
7395  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7396  {
7397  RetStr ="None, train terminated at " + Ptr->LocationName;
7398  }
7399  else if(Ptr->Command == "Frh")
7400  {
7401  RetStr = "None, train terminated at " + Ptr->LocationName;
7402  }
7403  else if(Ptr->Command == "Fer")
7404  {
7405  AnsiString AllowedExits = "";
7406  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at approx. " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime + TDateTime(DelayedRandMins/1440))) + AllowedExits;
7407  }
7408  else if(Ptr->Command == "Fjo")
7409  {
7410  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at approx. " +
7411  Utilities->Format96HHMM(GetTrainTime(11, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7412  }
7413  else if(Ptr->Command == "jbo")
7414  {
7415  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7416  " at approx. " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7417  }
7418  else if(Ptr->Command == "fsp")
7419  {
7420  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7421  " at approx. " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7422  }
7423  else if(Ptr->Command == "rsp")
7424  {
7425  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7426  " at approx. " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7427  }
7428  else if(Ptr->Command == "cdt")
7429  {
7430  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7431  }
7432  else if(Ptr->Command == "dsc")
7433  {
7434  RetStr = "Change description at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(65, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7435  }
7436  }
7437  else if(TrainController->TTClockTime > ActionTime) //condition added at v2.13.2 for trains that are delayed other than suffering a random delay
7438  {
7439  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7440  {
7441  throw Exception("Error - start entry in FloatingLabelNextString where TTClockTime > ActionTime");
7442  }
7443  if(Ptr->FormatType == TimeTimeLoc)
7444  {
7445  if(TrainMode == Timetable)
7446  {
7447  if(!TrainAtLocation(4, LocationName) || (LocationName != Ptr->LocationName))
7448  // not arrived yet in tt mode
7449  {
7450  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7451  }
7452  else
7453  {
7454  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7455  }
7456  }
7457  else // TrainMode == Signaller
7458  {
7459  if(!DepartureTimeSet) // not arrived yet
7460  {
7461  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7462  }
7463  else
7464  {
7465  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7466  }
7467  }
7468  }
7469  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7470  {
7471  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7472  }
7473  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7474  {
7475  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7476  }
7477  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7478  {
7479  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7480  }
7481  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7482  {
7483  RetStr = "Pass " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7484  }
7485  else if(Ptr->Command == "Fns")
7486  {
7487  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(60, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7488  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7489  RetStr = GetNewServiceDepartureInfo(19, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7490  }
7491  else if(Ptr->Command == "F-nshs")
7492  {
7493  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7495  RetStr = GetNewServiceDepartureInfo(20, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7496  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7497  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7498  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7499  }
7500  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7501  {
7502  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(61, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7503  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7504  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7505  RetStr = GetNewServiceDepartureInfo(21, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7506  }
7507  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7508  {
7509  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7510  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7511  RetStr = GetNewServiceDepartureInfo(22, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7512  }
7513  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7514  {
7515  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(62, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7516  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7517  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7518  RetStr = GetNewServiceDepartureInfo(23, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7519  }
7520  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7521  {
7522  RetStr ="None, train terminated at " + Ptr->LocationName;
7523  }
7524  else if(Ptr->Command == "Frh")
7525  {
7526  RetStr = "None, train terminated at " + Ptr->LocationName;
7527  }
7528  else if(Ptr->Command == "Fer")
7529  {
7530  AnsiString AllowedExits = "";
7531  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(5, Ptr->ExitList, AllowedExits) /*+ " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime)*/ + AllowedExits;
7532  }
7533  else if(Ptr->Command == "Fjo")
7534  {
7535  RetStr = "Join " + TrainController->GetRepeatHeadCode(63, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// + " at approx. " +
7536 // Utilities->Format96HHMM(TrainController->TTClockTime);
7537  }
7538  else if(Ptr->Command == "jbo")
7539  {
7540  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(64, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// +
7541 // " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7542  }
7543  else if(Ptr->Command == "fsp")
7544  {
7545  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(65, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7546  " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7547  }
7548  else if(Ptr->Command == "rsp")
7549  {
7550  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(66, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7551  " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7552  }
7553  else if(Ptr->Command == "cdt")
7554  {
7555  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7556  }
7557  else if(Ptr->Command == "dsc")
7558  {
7559  RetStr = "Change description at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7560  }
7561  }
7562  else //train not delayed
7563  {
7564  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7565  {
7566  throw Exception("Error - start entry in FloatingLabelNextString in final 'else'");
7567  }
7568  if(Ptr->FormatType == TimeTimeLoc)
7569  {
7570  if(TrainMode == Timetable)
7571  {
7572  if(!TrainAtLocation(3, LocationName) || (LocationName != Ptr->LocationName))
7573  // not arrived yet in tt mode
7574  {
7575  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(48, Ptr->ArrivalTime));
7576  }
7577  else
7578  {
7579  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7580  }
7581  }
7582  else // TrainMode == Signaller
7583  {
7584  if(!DepartureTimeSet) // not arrived yet
7585  {
7586  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(49, Ptr->ArrivalTime));
7587  }
7588  else
7589  {
7590  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7591  }
7592  }
7593  }
7594  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7595  {
7596  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(50, Ptr->ArrivalTime));
7597  }
7598  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7599  {
7600  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7601  }
7602  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7603  {
7604  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(51, Ptr->EventTime));
7605  }
7606  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7607  {
7608  RetStr = "Pass " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(52, Ptr->EventTime));
7609  }
7610  else if(Ptr->Command == "Fns")
7611  {
7612  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(53, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7613  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(53, Ptr->EventTime));
7614  RetStr = GetNewServiceDepartureInfo(10, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7615  }
7616  else if(Ptr->Command == "F-nshs")
7617  {
7618  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7620  RetStr = GetNewServiceDepartureInfo(12, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7621  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7622  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7623  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7624  }
7625  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7626  {
7627  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(54, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7628  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(55, Ptr->EventTime));
7629  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7630  RetStr = GetNewServiceDepartureInfo(14, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7631  }
7632  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7633  {
7634  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7635  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(56, Ptr->EventTime));
7636  RetStr = GetNewServiceDepartureInfo(16, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7637  }
7638  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7639  {
7640  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(55, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7641  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(57, Ptr->EventTime));
7642  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7643  RetStr = GetNewServiceDepartureInfo(18, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7644  }
7645  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7646  {
7647  RetStr ="None, train terminated at " + Ptr->LocationName;
7648  }
7649  else if(Ptr->Command == "Frh")
7650  {
7651  RetStr = "None, train terminated at " + Ptr->LocationName;
7652  }
7653  else if(Ptr->Command == "Fer")
7654  {
7655  AnsiString AllowedExits = "";
7656  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(4, Ptr->ExitList, AllowedExits) /*+ " at approx. " + Utilities->Format96HHMM(GetTrainTime(62, Ptr->EventTime))*/ + AllowedExits;
7657  }
7658  else if(Ptr->Command == "Fjo")
7659  {
7660  RetStr = "Join " + TrainController->GetRepeatHeadCode(56, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// + " at approx. " +
7661 // Utilities->Format96HHMM(GetTrainTime(58, Ptr->EventTime));
7662  }
7663  else if(Ptr->Command == "jbo")
7664  {
7665  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(57, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// +
7666 // " at approx. " + Utilities->Format96HHMM(GetTrainTime(59, Ptr->EventTime));
7667  }
7668  else if(Ptr->Command == "fsp")
7669  {
7670  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(58, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7671  " at approx. " + Utilities->Format96HHMM(GetTrainTime(60, Ptr->EventTime));
7672  }
7673  else if(Ptr->Command == "rsp")
7674  {
7675  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(59, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7676  " at approx. " + Utilities->Format96HHMM(GetTrainTime(61, Ptr->EventTime));
7677  }
7678  else if(Ptr->Command == "cdt")
7679  {
7680  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(63, Ptr->EventTime));
7681  }
7682  else if(Ptr->Command == "dsc")
7683  {
7684  RetStr = "Change description at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(66, Ptr->EventTime));
7685  }
7686  }
7687  Utilities->CallLogPop(1124);
7688  return(RetStr);
7689 }
7690 
7691 // ---------------------------------------------------------------------------
7692 /* as was
7693 AnsiString TTrain::FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
7694 {
7695  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7696  ",FloatingLabelNextString" + "," + HeadCode);
7697  AnsiString RetStr = "", LocationName = "";
7698 
7699  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7700  {
7701  throw Exception("Error - start entry in FloatingLabelNextString");
7702  }
7703  if(Ptr->FormatType == TimeTimeLoc)
7704  {
7705  if(TrainMode == Timetable)
7706  {
7707  if(!TrainAtLocation(, LocationName) || (LocationName != Ptr->LocationName))
7708  // not arrived yet in tt mode
7709  {
7710  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7711  }
7712  else
7713  {
7714  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7715  }
7716  }
7717  else // TrainMode == Signaller
7718  {
7719  if(!DepartureTimeSet) // not arrived yet
7720  {
7721  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7722  }
7723  else
7724  {
7725  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7726  }
7727  }
7728  }
7729  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7730  {
7731  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7732  }
7733  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7734  {
7735  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7736  }
7737  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7738  {
7739  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7740  }
7741  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7742  {
7743  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7744  }
7745  else if(Ptr->Command == "Fns")
7746  {
7747  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7748  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7749  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7750  }
7751  else if(Ptr->Command == "F-nshs")
7752  {
7753  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
7754  Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7755  RetStr = GetNewServiceDepartureInfo(, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7756  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7757  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7758  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7759  }
7760  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7761  {
7762  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7763  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7764  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7765  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7766  }
7767  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7768  {
7769  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7770  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7771  RetStr = GetNewServiceDepartureInfo(, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7772  }
7773  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7774  {
7775  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7776  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7777  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7778  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7779  }
7780  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7781  {
7782  RetStr ="None, train terminated at " + Ptr->LocationName;
7783  }
7784  else if(Ptr->Command == "Frh")
7785  {
7786  RetStr = "None, train terminated at " + Ptr->LocationName;
7787  }
7788  else if(Ptr->Command == "Fer")
7789  {
7790  AnsiString AllowedExits = "";
7791  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime)) + AllowedExits;
7792  }
7793  else if(Ptr->Command == "Fjo")
7794  {
7795  RetStr = "Join " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
7796  Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7797  }
7798  else if(Ptr->Command == "jbo")
7799  {
7800  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7801  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7802  }
7803  else if(Ptr->Command == "fsp")
7804  {
7805  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7806  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7807  }
7808  else if(Ptr->Command == "rsp")
7809  {
7810  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7811  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7812  }
7813  else if(Ptr->Command == "cdt")
7814  {
7815  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7816  }
7817  Utilities->CallLogPop();
7818  return(RetStr);
7819 }
7820 */
7821 // ---------------------------------------------------------------------------
7822 
7823 AnsiString TTrain::GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr, bool TimetableTime)
7824 { //last bool added at v2.13.2 so departure info adds random delay if actual rather than not timetable time required
7825  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
7826  + AnsiString(RptNum) + ",GetNewServiceDepartureInfo," + HeadCode);
7827  AnsiString DepTime = "", EventTime = "";
7828  bool CDTFlag = false; //reports if train changes direction before departs
7829  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
7830  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
7831  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
7832  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
7833  {
7834  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
7835  {
7836  TowardsLocation = AVI->LocationName;
7837  }
7838  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
7839  {
7840  TTrackElement TE = Track->TrackElementAt(1452, (AVI->ExitList.front()));
7841  if(TE.ActiveTrackElementName != "")
7842  {
7843  TowardsLocation = TE.ActiveTrackElementName;
7844  }
7845  else
7846  {
7847  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
7848  }
7849  }
7850  }
7851 
7852  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
7853  {
7854  if(AVI->Command == "cdt")
7855  {
7856  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
7857  continue;
7858  }
7859  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
7860  {
7861  TDateTime TTTime = TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes);
7862  if((DelayedRandMins >= 1) && !TimetableTime)
7863  {
7864  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7865  }
7866  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7867  {
7869  }
7870  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7871  {
7872  EventTime = Utilities->Format96HHMM(TTTime);
7873  }
7874  RetStr += "\nNew service splits at approx. " + EventTime;
7875  Utilities->CallLogPop(2234);
7876  return(RetStr);
7877  }
7878  if(AVI->Command == "jbo") //added at v2.15.0
7879  {
7880  TDateTime TTTime = TrainController->GetControllerTrainTime(28, AVI->EventTime, RptNum, IncrementalMinutes);
7881  if((DelayedRandMins >= 1) && !TimetableTime)
7882  {
7883  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7884  }
7885  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7886  {
7888  }
7889  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7890  {
7891  EventTime = Utilities->Format96HHMM(TTTime);
7892  }
7893  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at approx. " + EventTime;
7894  Utilities->CallLogPop(2595);
7895  return(RetStr);
7896  }
7897  if((AVI->Command == "Fns") || (AVI->Command == "F-nshs") || (AVI->Command == "Fns-sh")) //added at v2.15.0
7898  {
7899  TDateTime TTTime = TrainController->GetControllerTrainTime(29, AVI->EventTime, RptNum, IncrementalMinutes);
7900  if((DelayedRandMins >= 1) && !TimetableTime)
7901  {
7902  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7903  }
7904  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7905  {
7907  }
7908  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7909  {
7910  EventTime = Utilities->Format96HHMM(TTTime);
7911  }
7912  RetStr += "\nNew service finishes and forms another new service at approx. " + EventTime;
7913  Utilities->CallLogPop(2615);
7914  return(RetStr);
7915  }
7916  if(AVI->Command == "Fjo") //added at v2.15.0
7917  {
7918  TDateTime TTTime = TrainController->GetControllerTrainTime(26, AVI->EventTime, RptNum, IncrementalMinutes);
7919  if((DelayedRandMins >= 1) && !TimetableTime)
7920  {
7921  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7922  }
7923  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7924  {
7926  }
7927  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7928  {
7929  EventTime = Utilities->Format96HHMM(TTTime);
7930  }
7931  RetStr += "\nNew service finishes and joins " + AVI->OtherHeadCode + " at approx. " + EventTime;
7932  Utilities->CallLogPop(2605);
7933  return(RetStr);
7934  }
7935  if(AVI->Command == "Frh") //added at v2.15.0
7936  {
7937  RetStr += "\nNew service finishes and remains at the location.";
7938  Utilities->CallLogPop(2606);
7939  return(RetStr);
7940  }
7941  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
7942  {
7943  if(TimetableTime) //don't add random delay
7944  {
7945  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
7946  if(CDTFlag)
7947  {
7948  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7949  {
7950  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
7951  }
7952  else
7953  {
7954  RetStr += "\nNew service changes direction then departs at " + DepTime;
7955  }
7956  }
7957  else
7958  {
7959  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7960  {
7961  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
7962  }
7963  else
7964  {
7965  RetStr += "\nNew service departs at " + DepTime;
7966  }
7967  }
7968  }
7969  else if(DelayedRandMins >= 1)//add random delay
7970  {
7971  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(24, AVI->DepartureTime + TDateTime(DelayedRandMins/1440), RptNum, IncrementalMinutes));
7972  if(CDTFlag)
7973  {
7974  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7975  {
7976  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at approx. " + DepTime;
7977  }
7978  else
7979  {
7980  RetStr += "\nNew service changes direction then departs at approx. " + DepTime;
7981  }
7982  }
7983  else
7984  {
7985  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7986  {
7987  RetStr += "\nNew service departs towards " + TowardsLocation + " at approx. " + DepTime;
7988  }
7989  else
7990  {
7991  RetStr += "\nNew service departs at approx. " + DepTime;
7992  }
7993  }
7994  }
7995  else //no random delay but may be delayed for other reasons
7996  {
7997  TDateTime TTTime = TrainController->GetControllerTrainTime(25, AVI->DepartureTime, RptNum, IncrementalMinutes);
7998  if(TrainController->TTClockTime > TTTime)
7999  {
8001  }
8002  else
8003  {
8004  DepTime = Utilities->Format96HHMM(TTTime);
8005  }
8006  if(CDTFlag)
8007  {
8008  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
8009  {
8010  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at approx. " + DepTime;
8011  }
8012  else
8013  {
8014  RetStr += "\nNew service changes direction then departs at approx. " + DepTime;
8015  }
8016  }
8017  else
8018  {
8019  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
8020  {
8021  RetStr += "\nNew service departs towards " + TowardsLocation + " at approx. " + DepTime;
8022  }
8023  else
8024  {
8025  RetStr += "\nNew service departs at approx. " + DepTime;
8026  }
8027  }
8028  }
8029  Utilities->CallLogPop(2236);
8030  return(RetStr);
8031  }
8032  }
8033  Utilities->CallLogPop(2208);
8034  return(RetStr); //if reach here then RetStr doesn't change
8035 }
8036 
8037 // ---------------------------------------------------------------------------
8038 
8040 // Enter with Ptr pointing to first action to be listed (i.e. next action)
8041 // If there are actions to be skipped but a departure is awaited (SkippedDeparture = true) then after the departure Ptr moves forward by SkipPtrValue
8042 {
8043  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
8044  ",FloatingTimetableString" + "," + HeadCode);
8045  AnsiString RetStr = "", PartStr = "";
8046  int Count = 0;
8047  bool SkipDep = false, SkipDepActedOn = false; //SkipDepActedOn ensures only one SkipDep acted on
8048  AnsiString LocName = Ptr->LocationName;
8049 
8050  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
8051  // can start in signaller control so exclude this
8052  {
8053  throw Exception("Error - start entry in FloatingTimetableString");
8054  }
8055  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTimeLoc check later
8056  bool FirstPass = true;
8057  Ptr--; // because incremented at start of loop
8058 
8059  // different first TimeTimeLoc display if in signaller control
8060  do
8061  {
8062  Ptr++;
8063  if((Ptr->FormatType == Repeat) || TimetableFinished)
8064  {
8065  break;
8066  }
8067  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
8068  {
8069  AnsiString TrainLoc = "";
8070  if(TrainMode == Timetable)
8071  {
8072  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
8073  {
8074  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8075  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
8076  {
8077  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
8078  }
8079  }
8080  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
8081  {
8082  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
8083  }
8084  else
8085  {
8086  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
8087  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8088  Count++; // because there are 2 entries
8089  }
8090  }
8091  else // TrainMode == Signaller
8092  {
8093  if(DepartureTimeSet)
8094  {
8095  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8096  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
8097  {
8098  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
8099  }
8100  }
8101  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
8102  {
8103  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
8104  }
8105  else
8106  {
8107  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
8108  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8109  Count++; // because there are 2 entries
8110  }
8111  }
8112  }
8113  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
8114  {
8115  AnsiString TrainLoc = "";
8116  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
8117  {
8118  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8119  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
8120  {
8121  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
8122  }
8123  }
8124  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
8125  {
8126  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
8127  }
8128  else
8129  {
8130  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
8131  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8132  Count++; // because there are 2 entries
8133  }
8134  }
8135  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
8136  {
8137  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
8138  }
8139  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
8140  {
8141  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
8142  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
8143  {
8144  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
8145  }
8146  }
8147  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewService)
8148  { //note that TreatPassAsTimeLocDeparture can't be set if have SkippedDeparture
8149  PartStr = Utilities->Format96HHMM(GetTrainTime(47, Ptr->EventTime)) + ": Depart from " + Ptr->LocationName;
8150  }
8151  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
8152  {
8153  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
8154  }
8155  else if(Ptr->Command == "Fns")
8156  {
8157  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
8158  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8159  PartStr = GetNewServiceDepartureInfo(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to PartStr
8160  }
8161  else if(Ptr->Command == "F-nshs")
8162  {
8163  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
8164  Ptr->LocationName;
8165  PartStr = GetNewServiceDepartureInfo(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
8166  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
8167  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
8168  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
8169  }
8170  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
8171  {
8172  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
8173  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
8174  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
8175  PartStr = GetNewServiceDepartureInfo(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
8176  }
8177  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
8178  {
8179  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
8180  +" at " + Ptr->LocationName;
8181  PartStr = GetNewServiceDepartureInfo(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
8182  }
8183  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
8184  {
8185  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
8186  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
8187  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
8188  PartStr = GetNewServiceDepartureInfo(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
8189  }
8190  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
8191  {
8192  PartStr = "Terminate at " + Ptr->LocationName;
8193  }
8194  else if(Ptr->Command == "Frh")
8195  {
8196  PartStr = "Terminate at " + Ptr->LocationName;
8197  }
8198  else if(Ptr->Command == "Fer")
8199  {
8200  AnsiString AllowedExits = "";
8201  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
8202  }
8203  else if(Ptr->Command == "Fjo")
8204  {
8205  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
8206  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8207  }
8208  else if(Ptr->Command == "jbo")
8209  {
8210  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
8211  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8212  }
8213  else if(Ptr->Command == "fsp")
8214  {
8215  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
8216  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8217  if(Ptr->SplitDistribution != "")
8218  {
8219  PartStr+= ", split mass%-Power% = " + Ptr->SplitDistribution;
8220  }
8221  else
8222  {
8223  PartStr+= ", split mass%-Power% = 50-50";
8224  }
8225  }
8226  else if(Ptr->Command == "rsp")
8227  {
8228  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
8229  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8230  if(Ptr->SplitDistribution != "")
8231  {
8232  PartStr+= ", split mass%-Power% = " + Ptr->SplitDistribution;
8233  }
8234  else
8235  {
8236  PartStr+= ", split mass%-Power% = 50-50";
8237  }
8238  }
8239  else if(Ptr->Command == "cdt")
8240  {
8241  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
8242  }
8243  else if(Ptr->Command == "dsc")
8244  {
8245  PartStr = Utilities->Format96HHMM(GetTrainTime(67, Ptr->EventTime)) + ": Change description at " + Ptr->LocationName;
8246  }
8247  if(RetStr != "")
8248  {
8249  RetStr = RetStr + '\n' + PartStr;
8250  }
8251  else
8252  {
8253  RetStr = PartStr;
8254  }
8255  FirstPass = false;
8256  Count++;
8257 
8258  if(SkipDep)
8259  {
8260  Ptr = &(TrainDataEntryPtr->ActionVector.at(0)) + SkipPtrValue;
8261  Ptr--; //it is incremented at the start of the next loop
8262  SkipDep = false;
8263  SkipDepActedOn = true;
8264  }
8265  }
8266  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
8267  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
8268  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
8269  // forward as anyone should wish to see without looking at the full timetable
8270  if(TimetableFinished)
8271  {
8272  if(TrainMode == Timetable)
8273  {
8274  RetStr = "Timetable finished";
8275  }
8276  else
8277  {
8278  RetStr = "No timetable";
8279  }
8280  }
8281  Utilities->CallLogPop(1125);
8282  return("Timetable:\n" + RetStr);
8283 }
8284 
8285 // ---------------------------------------------------------------------------
8286 
8287 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
8288 {
8289  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
8290  Utilities->SaveFileString(OutFile, HeadCode);
8293  Utilities->SaveFileInt(OutFile, StartSpeed);
8296  Utilities->SaveFileInt(OutFile, RepeatNumber);
8299  Utilities->SaveFileInt(OutFile, Mass);
8302  Utilities->SaveFileDouble(OutFile, EntrySpeed);
8309  Utilities->SaveFileDouble(OutFile, BrakeRate);
8313  Utilities->SaveFileDouble(OutFile, double(EntryTime));
8314  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
8315  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
8316  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
8317  Utilities->SaveFileDouble(OutFile, double(TRSTime));
8318  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
8322  Utilities->SaveFileInt(OutFile, (short)TrainMode);
8327  Utilities->SaveFileBool(OutFile, Derailed);
8329  Utilities->SaveFileBool(OutFile, Crashed);
8336  Utilities->SaveFileBool(OutFile, NotInService);
8337  Utilities->SaveFileBool(OutFile, Plotted);
8338  Utilities->SaveFileBool(OutFile, TrainGone);
8339  Utilities->SaveFileBool(OutFile, SPADFlag);
8341  Utilities->SaveFileInt(OutFile, HOffset[0]);
8342  Utilities->SaveFileInt(OutFile, HOffset[1]);
8343  Utilities->SaveFileInt(OutFile, HOffset[2]);
8344  Utilities->SaveFileInt(OutFile, HOffset[3]);
8345  Utilities->SaveFileInt(OutFile, VOffset[0]);
8346  Utilities->SaveFileInt(OutFile, VOffset[1]);
8347  Utilities->SaveFileInt(OutFile, VOffset[2]);
8348  Utilities->SaveFileInt(OutFile, VOffset[3]);
8349  Utilities->SaveFileInt(OutFile, PlotElement[0]);
8350  Utilities->SaveFileInt(OutFile, PlotElement[1]);
8351  Utilities->SaveFileInt(OutFile, PlotElement[2]);
8352  Utilities->SaveFileInt(OutFile, PlotElement[3]);
8353  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
8354  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
8355  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
8356  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
8358  Utilities->SaveFileInt(OutFile, (short)Straddle);
8359  Utilities->SaveFileInt(OutFile, NextTrainID);
8360  Utilities->SaveFileInt(OutFile, TrainID);
8361  Utilities->SaveFileInt(OutFile, LeadElement);
8362  Utilities->SaveFileInt(OutFile, LeadEntryPos);
8363  Utilities->SaveFileInt(OutFile, LeadExitPos);
8364  Utilities->SaveFileInt(OutFile, MidElement);
8365  Utilities->SaveFileInt(OutFile, MidEntryPos);
8366  Utilities->SaveFileInt(OutFile, MidExitPos);
8367  Utilities->SaveFileInt(OutFile, LagElement);
8368  Utilities->SaveFileInt(OutFile, LagEntryPos);
8369  Utilities->SaveFileInt(OutFile, LagExitPos);
8370  int ColourNumber;
8371 
8373  {
8374  ColourNumber = 0;
8375  }
8377  {
8378  ColourNumber = 1;
8379  }
8381  {
8382  ColourNumber = 2;
8383  }
8385  {
8386  ColourNumber = 3;
8387  }
8389  {
8390  ColourNumber = 4;
8391  }
8393  {
8394  ColourNumber = 5;
8395  }
8397  {
8398  ColourNumber = 6;
8399  }
8401  {
8402  ColourNumber = 7;
8403  }
8405  {
8406  ColourNumber = 8;
8407  }
8409  {
8410  ColourNumber = 9;
8411  }
8413  {
8414  ColourNumber = 10;
8415  }
8417  {
8418  ColourNumber = 11;
8419  }
8421  {
8422  ColourNumber = 12;
8423  }
8424  else if(BackgroundColour == clTRSBackground)
8425  {
8426  ColourNumber = 13;
8427  }
8429  {
8430  ColourNumber = 14; // added at v2.4.0
8431  }
8432  Utilities->SaveFileInt(OutFile, ColourNumber);
8433 
8434  // additional data
8435  bool ForwardHeadCode;
8436 
8437  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
8438  {
8439  ForwardHeadCode = true;
8440  }
8441  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
8442  else
8443  {
8444  ForwardHeadCode = false;
8445  }
8446  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
8447 
8448  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
8449 
8450  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
8451  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
8452 
8453  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
8454  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
8455  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
8456  // so use the last asterisk position for this - 0 for false & 1 for true
8457  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
8458  AnsiString Marker;
8459 
8461  {
8462  Marker = "*****1";
8463  }
8464  else
8465  {
8466  Marker = "*****0";
8467  }
8468  if(RestoreTimetableLocation == "")
8469  {
8470  Utilities->SaveFileString(OutFile, Marker);
8471  }
8472  else
8473  {
8474  AnsiString CombinedString = RestoreTimetableLocation + Marker;
8475  Utilities->SaveFileString(OutFile, CombinedString);
8476  // RestoreTimetableLocation + marker
8477  }
8478  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
8479  Utilities->CallLogPop(1457);
8480 }
8481 
8482 // ---------------------------------------------------------------------------
8483 
8484 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
8485 {
8486  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
8487  HeadCode = Utilities->LoadFileString(InFile);
8490  StartSpeed = Utilities->LoadFileInt(InFile);
8492  if(SignallerMaxSpeed < 10)
8493  {
8494  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
8495  }
8497  RepeatNumber = Utilities->LoadFileInt(InFile);
8500  Mass = Utilities->LoadFileInt(InFile);
8503  {
8505  }
8506  // above added at v2.1.0 for legacy session files where value may not have been limited
8508  EntrySpeed = Utilities->LoadFileDouble(InFile);
8512  if(TimetableMaxRunningSpeed < 10)
8513  {
8514  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8515  }
8517  if(MaxRunningSpeed < 10)
8518  {
8519  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8520  }
8523  BrakeRate = Utilities->LoadFileDouble(InFile);
8527  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
8528  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
8529  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
8530  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
8531  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
8532  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
8541  Derailed = Utilities->LoadFileBool(InFile);
8543  Crashed = Utilities->LoadFileBool(InFile);
8550  NotInService = Utilities->LoadFileBool(InFile);
8551  Plotted = Utilities->LoadFileBool(InFile);
8552  TrainGone = Utilities->LoadFileBool(InFile);
8553  SPADFlag = Utilities->LoadFileBool(InFile);
8555  HOffset[0] = Utilities->LoadFileInt(InFile);
8556  HOffset[1] = Utilities->LoadFileInt(InFile);
8557  HOffset[2] = Utilities->LoadFileInt(InFile);
8558  HOffset[3] = Utilities->LoadFileInt(InFile);
8559  VOffset[0] = Utilities->LoadFileInt(InFile);
8560  VOffset[1] = Utilities->LoadFileInt(InFile);
8561  VOffset[2] = Utilities->LoadFileInt(InFile);
8562  VOffset[3] = Utilities->LoadFileInt(InFile);
8563  PlotElement[0] = Utilities->LoadFileInt(InFile);
8564  PlotElement[1] = Utilities->LoadFileInt(InFile);
8565  PlotElement[2] = Utilities->LoadFileInt(InFile);
8566  PlotElement[3] = Utilities->LoadFileInt(InFile);
8567  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
8568  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
8569  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
8570  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
8572  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
8573  NextTrainID = Utilities->LoadFileInt(InFile);
8574  // will be same for all but best to save all anyway
8575  TrainID = Utilities->LoadFileInt(InFile);
8576  LeadElement = Utilities->LoadFileInt(InFile);
8577  LeadEntryPos = Utilities->LoadFileInt(InFile);
8578  LeadExitPos = Utilities->LoadFileInt(InFile);
8579  MidElement = Utilities->LoadFileInt(InFile);
8580  MidEntryPos = Utilities->LoadFileInt(InFile);
8581  MidExitPos = Utilities->LoadFileInt(InFile);
8582  LagElement = Utilities->LoadFileInt(InFile);
8583  LagEntryPos = Utilities->LoadFileInt(InFile);
8584  LagExitPos = Utilities->LoadFileInt(InFile);
8585  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
8586 
8587  if(ColourNumber == 0)
8588  {
8590  }
8591  else if(ColourNumber == 1)
8592  {
8594  }
8595  else if(ColourNumber == 2)
8596  {
8598  }
8599  else if(ColourNumber == 3)
8600  {
8602  }
8603  else if(ColourNumber == 4)
8604  {
8606  }
8607  else if(ColourNumber == 5)
8608  {
8610  }
8611  else if(ColourNumber == 6)
8612  {
8614  }
8615  else if(ColourNumber == 7)
8616  {
8618  }
8619  else if(ColourNumber == 8)
8620  {
8622  }
8623  else if(ColourNumber == 9)
8624  {
8626  }
8627  else if(ColourNumber == 10)
8628  {
8630  }
8631  else if(ColourNumber == 11)
8632  {
8634  }
8635  else if(ColourNumber == 12)
8636  {
8638  }
8639  else if(ColourNumber == 13)
8640  {
8642  }
8643  else if(ColourNumber == 14)
8644  {
8645  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
8646 
8647  }
8648  // additional data
8650  // sets the BackgroundColour to the loaded value
8651  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
8652 
8653  if(ForwardHeadCode)
8654  {
8655  for(int x = 0; x < 4; x++)
8656  {
8658  }
8659  }
8660  else
8661  {
8662  for(int x = 0; x < 4; x++)
8663  {
8664  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
8665  }
8666  }
8667  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
8668  if(TrainMode == Timetable)
8669  {
8670  if(Crashed)
8671  {
8673  }
8674  else
8675  {
8677  }
8678  }
8679  else
8680  {
8682  }
8684  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
8685  if(Straddle == LeadMid)
8686  {
8687  if(LeadElement > -1)
8688  {
8690  }
8691  if(LeadElement > -1)
8692  {
8694  }
8695  if(MidElement > -1)
8696  {
8698  }
8699  if(MidElement > -1)
8700  {
8702  }
8703  }
8704  else if(Straddle == LeadMidLag)
8705  {
8706  if(LeadElement > -1)
8707  {
8709  }
8710  if(MidElement > -1)
8711  {
8713  }
8714  if(MidElement > -1)
8715  {
8717  }
8718  if(LagElement > -1)
8719  {
8721  }
8722  }
8723  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
8724 
8725  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
8726  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
8727 
8728  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
8729 
8730  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
8731  if(LeadElement > -1)
8732  // need to include this in case train exiting & no lead element
8733  {
8735  {
8736  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
8737  }
8738  }
8739  AValue = sqrt(2 * PowerAtRail / Mass);
8740 
8741  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
8742 
8743  // possible RestoreTimetableLocation + Marker, where Marker is
8744  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
8745  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
8746  // added at beta v0.2e
8747  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
8748  // name not allowed to include the '*' character
8749  {
8750  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
8751  bool GiveMessagesFalse = false;
8752  bool CheckLocationsExistInRailwayTrue = true;
8753  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
8754  {
8755  // otherwise take no action
8756  RestoreTimetableLocation = Location;
8757  }
8758  }
8759  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
8760 
8761  StoppedWithoutPower = false;
8762  if(Marker[6] == '1')
8763  {
8764  StoppedWithoutPower = true;
8765  }
8766  Utilities->CallLogPop(1458);
8767 }
8768 
8769 // ---------------------------------------------------------------------------
8770 
8771 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
8772 {
8774  {
8775  return(false); // HeadCode
8776 
8777  }
8778  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8779  {
8780  return(false); // RearStartElement
8781 
8782  }
8783  if(!Utilities->CheckFileInt(InFile, 0, 3))
8784  {
8785  return(false); // RearStartExitPos
8786 
8787  }
8788  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8789  {
8790  return(false); // StartSpeed
8791 
8792  }
8793  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8794  {
8795  return(false); // SignallerMaxSpeed
8796 
8797  }
8798  if(!Utilities->CheckFileBool(InFile))
8799  {
8800  return(false); // HoldAtLocationInTTMode
8801 
8802  }
8803  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8804  {
8805  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
8806 
8807  }
8808  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8809  {
8810  return(false); // IncrementalMinutes (max 96 x 60)
8811 
8812  }
8813  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8814  {
8815  return(false); // IncrementalDigits
8816 
8817  }
8818  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8819  {
8820  return(false); // Mass
8821 
8822  }
8823  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
8824  {
8825  return(false);
8826  }
8827  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
8828  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
8829  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8830  {
8831  return(false); // FrontElementLength
8832 
8833  }
8834  if(!Utilities->CheckFileDouble(InFile))
8835  {
8836  return(false); // EntrySpeed
8837 
8838  }
8839  if(!Utilities->CheckFileDouble(InFile))
8840  {
8841  return(false); // ExitSpeedHalf
8842 
8843  }
8844  if(!Utilities->CheckFileDouble(InFile))
8845  {
8846  return(false); // ExitSpeedFull
8847 
8848  }
8849  if(!Utilities->CheckFileDouble(InFile))
8850  {
8851  return(false); // TimetableMaxRunningSpeed
8852 
8853  }
8854  if(!Utilities->CheckFileDouble(InFile))
8855  {
8856  return(false); // MaxRunningSpeed
8857 
8858  }
8859  if(!Utilities->CheckFileDouble(InFile))
8860  {
8861  return(false); // MaxExitSpeed
8862 
8863  }
8864  if(!Utilities->CheckFileDouble(InFile))
8865  {
8866  return(false); // MaxBrakeRate
8867 
8868  }
8869  if(!Utilities->CheckFileDouble(InFile))
8870  {
8871  return(false); // BrakeRate
8872 
8873  }
8874  if(!Utilities->CheckFileDouble(InFile))
8875  {
8876  return(false); // PowerAtRail
8877 
8878  }
8879  if(!Utilities->CheckFileBool(InFile))
8880  {
8881  return(false); // FirstHalfMove
8882 
8883  }
8884  if(!Utilities->CheckFileBool(InFile))
8885  {
8886  return(false); // OneLengthAccelDecel
8887 
8888  }
8889  if(!Utilities->CheckFileDouble(InFile))
8890  {
8891  return(false); // double(EntryTime)
8892 
8893  }
8894  if(!Utilities->CheckFileDouble(InFile))
8895  {
8896  return(false); // double(ExitTimeHalf)
8897 
8898  }
8899  if(!Utilities->CheckFileDouble(InFile))
8900  {
8901  return(false); // double(ExitTimeFull)
8902 
8903  }
8904  if(!Utilities->CheckFileDouble(InFile))
8905  {
8906  return(false); // double(ReleaseTime)
8907 
8908  }
8909  if(!Utilities->CheckFileDouble(InFile))
8910  {
8911  return(false); // double(TRSTime)
8912 
8913  }
8914  if(!Utilities->CheckFileDouble(InFile))
8915  {
8916  return(false); // double(LastActionTime)
8917 
8918  }
8919  if(!Utilities->CheckFileBool(InFile))
8920  {
8921  return(false); // CallingOnFlag
8922 
8923  }
8924  if(!Utilities->CheckFileBool(InFile))
8925  {
8926  return(false); // BeingCalledOn
8927 
8928  }
8929  if(!Utilities->CheckFileBool(InFile))
8930  {
8931  return(false); // DepartureTimeSet
8932 
8933  }
8934  if(!Utilities->CheckFileInt(InFile, 0, 2))
8935  {
8936  return(false); // (short)TrainMode
8937 
8938  }
8939  if(!Utilities->CheckFileBool(InFile))
8940  {
8941  return(false); // TimetableFinished
8942 
8943  }
8944  if(!Utilities->CheckFileBool(InFile))
8945  {
8946  return(false); // LastActionDelayFlag
8947 
8948  }
8949  if(!Utilities->CheckFileBool(InFile))
8950  {
8951  return(false); // SignallerRemoved
8952 
8953  }
8954  if(!Utilities->CheckFileBool(InFile))
8955  {
8956  return(false); // TerminatedMessageSent
8957 
8958  }
8959  if(!Utilities->CheckFileBool(InFile))
8960  {
8961  return(false); // Derailed
8962 
8963  }
8964  if(!Utilities->CheckFileBool(InFile))
8965  {
8966  return(false); // DerailPending
8967 
8968  }
8969  if(!Utilities->CheckFileBool(InFile))
8970  {
8971  return(false); // Crashed
8972 
8973  }
8974  if(!Utilities->CheckFileBool(InFile))
8975  {
8976  return(false); // StoppedAtBuffers
8977 
8978  }
8979  if(!Utilities->CheckFileBool(InFile))
8980  {
8981  return(false); // StoppedAtSignal
8982 
8983  }
8984  if(!Utilities->CheckFileBool(InFile))
8985  {
8986  return(false); // StoppedAtLocation
8987 
8988  }
8989  if(!Utilities->CheckFileBool(InFile))
8990  {
8991  return(false); // SignallerStopped
8992 
8993  }
8994  if(!Utilities->CheckFileBool(InFile))
8995  {
8996  return(false); // StoppedAfterSPAD
8997 
8998  }
8999  if(!Utilities->CheckFileBool(InFile))
9000  {
9001  return(false); // StoppedForTrainInFront
9002 
9003  }
9004  if(!Utilities->CheckFileBool(InFile))
9005  {
9006  return(false); // NotInService
9007 
9008  }
9009  if(!Utilities->CheckFileBool(InFile))
9010  {
9011  return(false); // Plotted
9012 
9013  }
9014  if(!Utilities->CheckFileBool(InFile))
9015  {
9016  return(false); // TrainGone
9017 
9018  }
9019  if(!Utilities->CheckFileBool(InFile))
9020  {
9021  return(false); // SPADFlag
9022 
9023  }
9024  if(!Utilities->CheckFileBool(InFile))
9025  {
9026  return(false); // TimeTimeLocArrived
9027 
9028  }
9029  if(!Utilities->CheckFileInt(InFile, 0, 15))
9030  {
9031  return(false); // HOffset[0]
9032 
9033  }
9034  if(!Utilities->CheckFileInt(InFile, 0, 15))
9035  {
9036  return(false); // HOffset[1]
9037 
9038  }
9039  if(!Utilities->CheckFileInt(InFile, 0, 15))
9040  {
9041  return(false); // HOffset[2]
9042 
9043  }
9044  if(!Utilities->CheckFileInt(InFile, 0, 15))
9045  {
9046  return(false); // HOffset[3]
9047 
9048  }
9049  if(!Utilities->CheckFileInt(InFile, 0, 15))
9050  {
9051  return(false); // VOffset[0]
9052 
9053  }
9054  if(!Utilities->CheckFileInt(InFile, 0, 15))
9055  {
9056  return(false); // VOffset[1]
9057 
9058  }
9059  if(!Utilities->CheckFileInt(InFile, 0, 15))
9060  {
9061  return(false); // VOffset[2]
9062 
9063  }
9064  if(!Utilities->CheckFileInt(InFile, 0, 15))
9065  {
9066  return(false); // VOffset[3]
9067 
9068  }
9069  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9070  {
9071  return(false); // PlotElement[0]
9072 
9073  }
9074  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9075  {
9076  return(false); // PlotElement[1]
9077 
9078  }
9079  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9080  {
9081  return(false); // PlotElement[2]
9082 
9083  }
9084  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9085  {
9086  return(false); // PlotElement[3]
9087 
9088  }
9089  if(!Utilities->CheckFileInt(InFile, 0, 3))
9090  {
9091  return(false); // PlotEntryPos[0]
9092 
9093  }
9094  if(!Utilities->CheckFileInt(InFile, 0, 3))
9095  {
9096  return(false); // PlotEntryPos[1]
9097 
9098  }
9099  if(!Utilities->CheckFileInt(InFile, 0, 3))
9100  {
9101  return(false); // PlotEntryPos[2]
9102 
9103  }
9104  if(!Utilities->CheckFileInt(InFile, 0, 3))
9105  {
9106  return(false); // PlotEntryPos[3]
9107 
9108  }
9109  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9110  {
9111  return(false); // TrainCrashedInto
9112 
9113  }
9114  if(!Utilities->CheckFileInt(InFile, 0, 2))
9115  {
9116  return(false); // (short)Straddle
9117 
9118  }
9119  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
9120  {
9121  return(false); // NextTrainID
9122 
9123  }
9124  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
9125  {
9126  return(false); // TrainID
9127 
9128  }
9129  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9130  {
9131  return(false); // LeadElement
9132 
9133  }
9134  if(!Utilities->CheckFileInt(InFile, 0, 3))
9135  {
9136  return(false); // LeadEntryPos
9137 
9138  }
9139  if(!Utilities->CheckFileInt(InFile, 0, 3))
9140  {
9141  return(false); // LeadExitPos
9142 
9143  }
9144  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9145  {
9146  return(false); // MidElement
9147 
9148  }
9149  if(!Utilities->CheckFileInt(InFile, 0, 3))
9150  {
9151  return(false); // MidEntryPos
9152 
9153  }
9154  if(!Utilities->CheckFileInt(InFile, 0, 3))
9155  {
9156  return(false); // MidExitPos
9157 
9158  }
9159  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
9160  {
9161  return(false); // LagElement
9162 
9163  }
9164  if(!Utilities->CheckFileInt(InFile, 0, 3))
9165  {
9166  return(false); // LagEntryPos
9167 
9168  }
9169  if(!Utilities->CheckFileInt(InFile, 0, 3))
9170  {
9171  return(false); // LagExitPos
9172 
9173  }
9174  if(!Utilities->CheckFileInt(InFile, 0, 14))
9175  {
9176  return(false);
9177  }
9178  // Background colour number //14 is failed colour at v2.4.0
9179  if(!Utilities->CheckFileBool(InFile))
9180  {
9181  return(false); // ForwardHeadCode
9182 
9183  }
9184  if(!Utilities->CheckFileInt(InFile, 0, 10000))
9185  {
9186  return(false); // TrainDataEntryValue
9187 
9188  }
9189  if(!Utilities->CheckFileInt(InFile, 0, 10000))
9190  {
9191  return(false); // ActionVectorEntryValue
9192 
9193  }
9195  {
9196  return(false); // End of train marker + possible RestoreTimetableLocation
9197 
9198  }
9199  // and StoppedWithoutPower flag
9200  return(true);
9201 }
9202 
9203 // ---------------------------------------------------------------------------
9204 
9205 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
9206 {
9207  // order below reflects significance so earlier shows first, as may have more than one flag set
9208  // only plot flashing trains when Flash is true
9209 
9210 /*
9211  clCrashedBackground (TColor)0x0000FF red
9212  clDerailedBackground (TColor)0x0000FF red
9213  clSPADBackground (TColor)0x00FFFF yellow
9214  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
9215  clCallOnBackground (TColor)0xFF33FF light magenta
9216  clSignalStopBackground (TColor)0x00FF66 green
9217  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
9218  clStationStopBackground (TColor)0xCCFFCC pale green
9219  clTRSBackground (TColor)0xFFCCFF light pink
9220  clBufferStopBackground (TColor)0xFFFFCC pale cyan
9221  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
9222  clSignallerStopped (TColor)0x99CCFF caramel
9223  clNormalBackground (TColor)0xCCCCCC grey
9224 */
9225 
9226  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
9227  bool HideFlashingTrain = true;
9228  // hide it when Flash false so it blinks on and off
9229  // if don't hide it it stays displayed all the time
9230  Graphics::TBitmap *SmallTrainBitmap;
9231 
9232  // NB ensure retain same order as zoomed in order so colours correspond
9234  {
9235  TrainController->CrashWarning = true;
9236  SmallTrainBitmap = RailGraphics->smRed;
9237  }
9239  {
9241  SmallTrainBitmap = RailGraphics->smRed;
9242  }
9244  {
9245  TrainController->SPADWarning = true;
9246  SmallTrainBitmap = RailGraphics->smYellow;
9247  }
9249  {
9251  SmallTrainBitmap = RailGraphics->smOrange;
9252  }
9254  {
9256  SmallTrainBitmap = RailGraphics->smMagenta;
9257  }
9259  {
9261  SmallTrainBitmap = RailGraphics->smBrightGreen;
9262  }
9264  {
9266  SmallTrainBitmap = RailGraphics->smCyan;
9267  }
9269  {
9270  SmallTrainBitmap = RailGraphics->smPaleGreen;
9271  HideFlashingTrain = false;
9272  }
9274  {
9275  SmallTrainBitmap = RailGraphics->smCyan;
9276  HideFlashingTrain = false;
9277  }
9279  {
9280  SmallTrainBitmap = RailGraphics->smLightBlue;
9281  HideFlashingTrain = false;
9282  }
9284  {
9285  SmallTrainBitmap = RailGraphics->smCaramel;
9286  HideFlashingTrain = false;
9287  }
9288  else
9289  {
9290  SmallTrainBitmap = RailGraphics->smBlack; // moving
9291  HideFlashingTrain = false;
9292  }
9293  // now plot the new train
9294  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
9295  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
9296  {
9297  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
9298  }
9299  if((MidElement > -1) && (!HideFlashingTrain || Flash))
9300  {
9301  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
9302  }
9303  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
9304  {
9305  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
9306  }
9310  Utilities->CallLogPop(1459);
9311 }
9312 
9313 // ---------------------------------------------------------------------------
9314 
9316 {
9317  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
9318  if(!Display->ZoomOutFlag)
9319  {
9320  Utilities->CallLogPop(1304);
9321  return;
9322  }
9323  for(int y = 0; y < 3; y++)
9324  {
9325  if(OldZoomOutElement[y] > -1)
9326  {
9327  bool FoundFlag = false;
9328  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
9329  TTrackElement IATElement1, IATElement2;
9330  // default elements to begin with
9331  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
9332  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
9333  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
9334  if(FoundFlag)
9335  {
9336  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
9337  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
9338  if(IMPair.first != IMPair.second)
9339  {
9340  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
9341  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
9342  }
9343  }
9344  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
9345  }
9346  }
9347  Utilities->CallLogPop(1305);
9348 }
9349 
9350 // ---------------------------------------------------------------------------
9351 
9352 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
9353 {
9354  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
9355  LocationName = "";
9356  if(!RevisedStoppedAtLoc())
9357  {
9358  Utilities->CallLogPop(1398);
9359  return(false);
9360  }
9361  if(LeadElement > -1)
9362  {
9364  }
9365  if((LocationName == "") && (MidElement > -1))
9366  {
9367  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
9368  }
9369  if((LocationName == "") && (LagElement > -1))
9370  {
9371  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
9372  }
9373  if(LocationName == "")
9374  {
9375  throw Exception("Error - Location name not set in TrainAtLocation");
9376  }
9377  Utilities->CallLogPop(1399);
9378  return(true);
9379 }
9380 
9381 // ---------------------------------------------------------------------------
9382 
9383 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
9384 {
9385  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
9386  for(int x = 0; x < 4; x++)
9387  {
9388  PlotTrainGraphic(7, x, Disp);
9389  }
9390  Utilities->CallLogPop(647);
9391 }
9392 
9393 // ---------------------------------------------------------------------------
9394 
9395 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
9396 {
9397  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
9398  for(int x = 0; x < 4; x++)
9399  {
9400  if(PlotElement[x] > -1)
9401  {
9402  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
9403  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
9404  }
9405  }
9406  Utilities->CallLogPop(1708);
9407 }
9408 
9409 // ---------------------------------------------------------------------------
9410 
9411 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
9412 {
9413  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
9414  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
9415  AnsiString(LinkNumber) + "," + HeadCode);
9416 
9417 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
9418  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
9419  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
9420 */
9421 
9422  // note that MidElement always fully occupied
9423  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
9424  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
9425  {
9426  Utilities->CallLogPop(2005);
9427  return(true);
9428  }
9429  if(Straddle == LeadMid)
9430  {
9431  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
9432  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
9433  {
9434  Utilities->CallLogPop(2006);
9435  return(true);
9436  }
9437  }
9438  else if(Straddle == LeadMidLag)
9439  {
9440  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
9441  // only interested in LeadEntryPos as train not occupying ExitPos yet
9442  {
9443  Utilities->CallLogPop(2007);
9444  return(true);
9445  }
9446  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
9447  // only interested in LagExitPos as train has left EntryPos
9448  {
9449  Utilities->CallLogPop(2008);
9450  return(true);
9451  }
9452  }
9453  Utilities->CallLogPop(2009);
9454  return(false);
9455 }
9456 
9457 // ---------------------------------------------------------------------------
9458 
9459 float TTrain::CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair) // only called for running trains.
9464 // TimeToExit & ExitPair added for multiplayer
9465 {
9466  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
9467  int DistanceToRedSignal = 0, DistanceToExit = -1;
9468  float TimeToAct = 0, LastTimeToExit = TimeToExit;
9469  TimeToExit = -1;
9470  ExitPair.first = -1;
9471  ExitPair.second = -1;
9472  float MinsEarly = 0; //added at v2.6.1
9473  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
9474  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
9475  float TempTTE;
9476 
9477  if(TrainFailed)
9478  {
9479  Utilities->CallLogPop(2147);
9480  return(0); // time to act now, time to exit set above
9481  }
9482  if(SignallerStopped)
9483  {
9484  Utilities->CallLogPop(2080);
9485  return(-1); //time to exit set above
9486  }
9487  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
9488  {
9489  Utilities->CallLogPop(2266);
9490  return(-1); // time to exit set above
9491  }
9492 
9493  // check if exiting at a continuation, if so there's no action time needed but still need exit time & ExitPair
9494  if((LeadElement == -1) && (MidElement == -1) && (LagElement > -1) && (Track->TrackElementAt(1411, LagElement).TrackType == Continuation)
9495  /*&& (ExitSpeedFull > 1)*/) //LagElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9496  {
9497  if(Straddle == LeadMidLag) //only half of rear train element on exit, 0.5 lengths to exit
9498  {
9499  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9500  {
9501  TempTTE = (0.5 * Track->TrackElementAt(1412, LagElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9502  if(TempTTE < LastTimeToExit)
9503  {
9504  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9505  }
9506  else
9507  {
9508  TimeToExit = LastTimeToExit;
9509  }
9510  }
9511  else
9512  {
9513  TimeToExit = LastTimeToExit;
9514  }
9515  ExitPair.first = Track->TrackElementAt(1413, LagElement).HLoc;
9516  ExitPair.second = Track->TrackElementAt(1414, LagElement).VLoc;
9517  Utilities->CallLogPop(2342);
9518  return(-1);
9519  }
9520  else
9521  {
9522  TimeToExit = 0; //all train exited
9523  ExitPair.first = Track->TrackElementAt(1415, LagElement).HLoc;
9524  ExitPair.second = Track->TrackElementAt(1416, LagElement).VLoc;
9525  Utilities->CallLogPop(2343);
9526  return(-1);
9527  }
9528  }
9529  if((LeadElement == -1) && (MidElement > -1) && (Track->TrackElementAt(1417, MidElement).TrackType == Continuation)/* && (ExitSpeedFull > 1)*/)
9530  //here MidElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9531  {
9532  if(Straddle == LeadMidLag) //front element of train half off the exit, 1.5 lengths to exit
9533  {
9534  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9535  {
9536  TempTTE = (1.5 * Track->TrackElementAt(1418, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9537  if(TempTTE < LastTimeToExit)
9538  {
9539  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9540  }
9541  else
9542  {
9543  TimeToExit = LastTimeToExit;
9544  }
9545  }
9546  else
9547  {
9548  TimeToExit = LastTimeToExit;
9549  }
9550  ExitPair.first = Track->TrackElementAt(1419, MidElement).HLoc;
9551  ExitPair.second = Track->TrackElementAt(1420, MidElement).VLoc;
9552  Utilities->CallLogPop(2344);
9553  return(-1);
9554  }
9555  else //front element of train fully off the exit, one length to exit
9556  {
9557  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9558  {
9559  TempTTE = (Track->TrackElementAt(1421, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9560  if(TempTTE < LastTimeToExit)
9561  {
9562  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9563  }
9564  else
9565  {
9566  TimeToExit = LastTimeToExit;
9567  }
9568  }
9569  else
9570  {
9571  TimeToExit = LastTimeToExit;
9572  }
9573  ExitPair.first = Track->TrackElementAt(1422, MidElement).HLoc;
9574  ExitPair.second = Track->TrackElementAt(1423, MidElement).VLoc;
9575  Utilities->CallLogPop(2345);
9576  return(-1);
9577  }
9578  }
9579  if(LeadElement > -1)
9580  {
9582  /* && (ExitSpeedFull > 1)*/) //LeadElement is the exit. If LeadMidLag have lead half on exit or if LeadMid have lead full on exit
9583  { //if LeadMidLag have 2.5 lengths to exit, else have 2 lengths to exit
9584  //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9585  if(Straddle == LeadMidLag)
9586  {
9587  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9588  {
9589  TempTTE = (2.5 * Track->TrackElementAt(1426, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9590  if(TempTTE < LastTimeToExit)
9591  {
9592  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9593  }
9594  else
9595  {
9596  TimeToExit = LastTimeToExit;
9597  }
9598  }
9599  else
9600  {
9601  TimeToExit = LastTimeToExit;
9602  }
9603  ExitPair.first = Track->TrackElementAt(1427, LeadElement).HLoc;
9604  ExitPair.second = Track->TrackElementAt(1428, LeadElement).VLoc;
9605  Utilities->CallLogPop(2346);
9606  return(-1);
9607  }
9608  else
9609  {
9610  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9611  {
9612  TempTTE = (2 * Track->TrackElementAt(1429, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9613  if(TempTTE < LastTimeToExit)
9614  {
9615  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9616  }
9617  else
9618  {
9619  TimeToExit = LastTimeToExit;
9620  }
9621  }
9622  else
9623  {
9624  TimeToExit = LastTimeToExit;
9625  }
9626  ExitPair.first = Track->TrackElementAt(1430, LeadElement).HLoc;
9627  ExitPair.second = Track->TrackElementAt(1431, LeadElement).VLoc;
9628  Utilities->CallLogPop(2347);
9629  return(-1);
9630  }
9631  }
9632  }
9633 //here if LeadElement > -1 and its forward connection also > -1
9634 
9635  // calc distance to next red signal
9636  if(!Stopped() || RevisedStoppedAtLoc())
9637  {
9638  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
9639  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
9640  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
9641 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
9642  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
9643  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
9644  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
9645  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
9646  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
9647  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
9648  before the train has stopped the current station is still recognised as a future stop.
9649  In signaller mode stops don't count, and if pass stop signal command is given then when have LeadMidLag the current element
9650  becomes the signal, and the time to act indication becomes 'NOW'.
9651 */
9652  {
9653  FirstPosToBeMeasured = LeadElement;
9654  FirstEntryPos = LeadEntryPos;
9655  }
9656  float CurrentStopTime; // set to 0 at start of function
9657  float LaterStopTime; // set to 0 at start of function
9658  float RecoverableTime; // set to 0 at start of function
9659  int AvTrackSpeed; // set to zero at start of function
9660  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
9661  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
9662  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
9663 //at this point can't have both DistanceToRedSignal and DistanceToExit both set. Either both will be unset (-1) or one will be set.
9664 //Therefore since need to calculate the time for each in the same way use a generic
9665 
9666  if((DistanceToRedSignal == -1) && (DistanceToExit == -1))// both unset so no action needed
9667  {
9668  TimeToExit = -1;
9669  Utilities->CallLogPop(2076);
9670  return(-1);
9671  }
9672 //else one or other is set
9673  bool DistanceToRedSignalSet = (DistanceToRedSignal > -1);
9674  bool DistanceToExitSet = (DistanceToExit > -1);
9675  int GenericDistance = DistanceToRedSignal;
9676  if(DistanceToExitSet)
9677  {
9678  GenericDistance = DistanceToExit;
9679  }
9680 /* Have MinsDelayed; pos or neg,
9681  CurrentStopTime; pos or zero
9682  LaterStopTime; pos or zero
9683  RecoverableTime; pos or zero
9684 
9685  & from these calculate TotalStopTime. noting that:
9686  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
9687  RecoverableTime always < LaterStopTime or both zero
9688  can't subtract more than RecoverableTime (MinsDelayed > 0)
9689  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
9690  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
9691  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
9692  if running early & stopped at location CurrentStopTime will automatically include the excess
9693 */
9694  float TimeToSubtract, TotalStopTime;
9695  if(MinsDelayed > RecoverableTime)
9696  {
9697  TimeToSubtract = RecoverableTime;
9698  }
9699  else
9700  {
9701  TimeToSubtract = MinsDelayed; // may be negative;
9702  }
9703  if((AvTrackSpeed > 0) && (DistanceToStationStop <= GenericDistance) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
9704  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
9705  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
9706  //next station stop
9707  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
9708  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
9709  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
9710  //first find departure time from the next stop
9711  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
9712  {
9713  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
9714  {
9717  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
9718  }
9719  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
9720  {
9722  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
9723  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
9724  {
9725  // must be a departure
9726  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
9727  }
9728  }
9729  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
9730  {
9731  MinsEarly = 0;
9732  }
9733  if(MinsEarly < 0)
9734  {
9735  MinsEarly = 0;
9736  }
9737  }
9738  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
9739  {
9740  if(CurrentStopTime > 0)
9741  {
9742  TotalStopTime = CurrentStopTime + LaterStopTime;
9743  }
9744  // stopped at loc, will depart on time
9745  else
9746  {
9747  TotalStopTime = LaterStopTime - MinsDelayed;
9748  }
9749  // not stopped, will depart on time at first later stop so add the delay
9750  }
9751  else if((MinsEarly > 0) && !Stopped()) //running early
9752  {
9753  TotalStopTime = LaterStopTime + MinsEarly;
9754  }
9755  else // on time or running late
9756  {
9757  if(LaterStopTime == 0)
9758  {
9759  TotalStopTime = CurrentStopTime;
9760  }
9761  // no later stops, if stopped now will depart as soon as possible,
9762  // if not stopped no stop times to add
9763  else
9764  {
9765  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
9766  }
9767  }
9768  if(AvTrackSpeed < 30)
9769  {
9770  AvTrackSpeed = 30;
9771  }
9772  int Speed = AvTrackSpeed;
9773  if(AvTrackSpeed > int(MaxRunningSpeed))
9774  {
9775  Speed = int(MaxRunningSpeed);
9776  }
9777  if(TrainMode == Signaller)
9778  {
9779  Speed = SignallerMaxSpeed;
9780  TotalStopTime = 0;
9781  }
9782  if(DistanceToRedSignalSet)
9783  {
9784  TimeToAct = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9785  // accel & decel taken into account in
9786  // CalcDistanceToRedSignalandStopTime
9787  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9788  TimeToExit = -1;
9789  Utilities->CallLogPop(2079);
9790  return(TimeToAct);
9791  }
9792  else //DistanceToExitSet must be true
9793  {
9794  TimeToExit = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9795  // accel & decel taken into account in
9796  // CalcDistanceToRedSignalandStopTime
9797  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9798  Utilities->CallLogPop(2370);
9799  return(-1); //no red signal so no time to act
9800  }
9801  }
9802  else // stopped not at location
9803  {
9805  {
9806  TimeToAct = 0.0;
9807  TimeToExit = -1;
9808  }
9809  if(StoppedWithoutPower) //added at v2.13.2 as this situation was missed & time to act was 0 [If train failed then covered above]
9810  {
9811  TimeToAct = -1;
9812  TimeToExit = -1;
9813  }
9814  // but if stopped at a signal & autosigs route after it then ok, provided signal not failed
9815  if(StoppedAtSignal)
9816  {
9817  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
9818  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
9819  bool NextElementFailed = Track->TrackElementAt(1548, NextElement).Failed; //added at v2.13.2
9820  int NextExitPos;
9821  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
9822  {
9823  if((NextEntryPos == 0) || (NextEntryPos == 2))
9824  // leading entry point
9825  {
9826  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
9827  {
9828  NextExitPos = 1;
9829  }
9830  else
9831  {
9832  NextExitPos = 3;
9833  }
9834  }
9835  else
9836  {
9837  NextExitPos = 0; // trailing entry point
9838  }
9839  }
9840  else
9841  {
9842  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
9843  }
9844  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
9845  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
9846  int RouteNumber; // holder for referenced value, not used
9847  if((AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !NextElementFailed)
9848  { //NextElementFailed added at v2.13.2
9849  TimeToAct = -1;
9850  TimeToExit = -1;
9851  }
9852  }
9853  Utilities->CallLogPop(2074);
9854  return(TimeToAct);
9855  }
9856 }
9857 
9858 // ---------------------------------------------------------------------------
9859 
9861 {
9862  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
9863  if(LeadElement > -1)
9864  {
9866  {
9867  Utilities->CallLogPop(2148);
9868  return(true);
9869  }
9870  }
9871  if(MidElement > -1)
9872  {
9874  {
9875  Utilities->CallLogPop(2149);
9876  return(true);
9877  }
9878  }
9879  if(LagElement > -1)
9880  {
9882  {
9883  Utilities->CallLogPop(2150);
9884  return(true);
9885  }
9886  }
9887  Utilities->CallLogPop(2151);
9888  return(false);
9889 }
9890 
9891 // ---------------------------------------------------------------------------
9892 // TTrainController
9893 // ---------------------------------------------------------------------------
9894 
9896 {
9897  OnTimeArrivals = 0;
9898  LateArrivals = 0;
9899  EarlyArrivals = 0;
9900  OnTimePasses = 0;
9901  LatePasses = 0;
9902  EarlyPasses = 0;
9903  OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
9904  LateExits = 0;
9905  EarlyExits = 0;
9906  OnTimeDeps = 0;
9907  LateDeps = 0;
9908  MissedStops = 0;
9909  OtherMissedEvents = 0;
9910  UnexpectedExits = 0;
9911  NumFailures = 0;
9912  IncorrectExits = 0;
9913  SPADEvents = 0;
9914  SPADRisks = 0;
9915  CrashedTrains = 0;
9916  Derailments = 0;
9917  TotArrDepPass = 0;
9918  TotLateArrMins = 0;
9919  TotEarlyArrMins = 0;
9920  TotLatePassMins = 0;
9921  TotEarlyPassMins = 0;
9922  TotLateExitMins = 0; //added at v2.9.1
9923  TotEarlyExitMins = 0; //added at v2.9.1
9924  TotLateDepMins = 0;
9925  ExcessLCDownMins = 0;
9926  SkippedTTEvents = 0; //added at v2.11.0
9927  TTClockTime = 0; // added for v0.6
9929  // added at v1.3.0 to ensure false at start
9930  OpTimeToActUpdateCounter = 0; // new v2.2.0
9931  OpActionPanelVisible = false; // new v2.2.0
9932  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
9933  SSHigh = false;
9934  MRSHigh = false;
9935  MRSLow = false;
9936  MassHigh = false;
9937  BFHigh = false;
9938  BFLow = false;
9939  PwrHigh = false;
9940  SigSHigh = false;
9941  SigSLow = false;
9942  randomize();
9943  // to seed rand() & random() with a random number (see UpdateTrain)
9944 }
9945 
9946 // ---------------------------------------------------------------------------
9947 
9949 {
9950  for(unsigned int x = 0; x < TrainVector.size(); x++)
9951  {
9952  TrainVectorAt(32, x).DeleteTrain(4);
9953  }
9954  TrainVector.clear();
9955 }
9956 
9957 // ---------------------------------------------------------------------------
9958 
9959 void TTrainController::LogEvent(AnsiString Str)
9960 {
9961  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
9962 
9963  // restrict to last 1000 entries
9964  Utilities->EventLog.push_back(FullStr);
9965  if(Utilities->EventLog.size() > 1000)
9966  {
9967  Utilities->EventLog.pop_front();
9968  }
9969 }
9970 
9971 // ---------------------------------------------------------------------------
9972 
9974 {
9975  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
9976  bool ClockState = Utilities->Clock2Stopped;
9977  Utilities->Clock2Stopped = true;
9978  // new section dealing with Snt & Snt-sh additions
9979  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
9980  // clock tick after stops flashing
9982  {
9983  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9984  {
9985  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
9986  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
9987  TActionEventType EventType = NoEvent;
9988  if(AVEntry0.Command == "Snt")
9989  {
9990  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9991  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9992  int IncrementalMinutes = 0;
9993  int IncrementalDigits = 0;
9994  if(AVEntryLast.FormatType == Repeat)
9995  {
9996  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9997  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9998  }
9999  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
10000  {
10001  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
10002  }
10003  // see above note
10004 
10005  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
10006  {
10007  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
10008  if(TTOD.RunningEntry != NotStarted)
10009  {
10010  continue;
10011  }
10012 
10013 //Multiplayer: here check for a train entering at a coupling {RearStartOrRepeatMins shows if it's a coupling or not), and can only be a Snt entry
10014 //if so and no arrival signalled yet bypass the timetabled arrival
10015 //if so and arrival signalled then start the new service, using the repeat number and headcode for the entering train
10016 //if a repeat is skipped then should be ok if it arrives later as its RunningEntry is still NotStarted
10017 
10018  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
10019  {
10020  break; // all the rest will also be greater
10021  }
10022  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
10023  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
10024  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
10025  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
10026  {
10027  TTOD.TrainID = TrainVector.back().TrainID;
10028  TTOD.RunningEntry = Running;
10029  }
10030  else if(EventType == FailTrainEntry)
10031  {
10032  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
10033  }
10034  }
10035  }
10036  if(AVEntry0.Command == "Snt-sh")
10037  // just start this once, shuttle repeats take care of restarts
10038  {
10039  // calc below only for Snt & Snt-sh entries rather than all entries to save time
10040  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
10041  int IncrementalMinutes = 0;
10042  int IncrementalDigits = 0;
10043  if(AVEntryLast.FormatType == Repeat)
10044  {
10045  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
10046  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
10047  }
10048  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
10049  {
10050  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
10051  }
10052  // see above note
10053  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
10054  if(TTOD.RunningEntry == NotStarted)
10055  {
10056  if(AVEntry0.EventTime <= TTClockTime)
10057  {
10058  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
10059  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
10060  TDEntry.SignallerSpeed, false, EventType))
10061  // false for SignallerControl
10062  {
10063  TTOD.TrainID = TrainVector.back().TrainID;
10064  TTOD.RunningEntry = Running;
10065  }
10066  else if(EventType == FailTrainEntry)
10067  {
10068  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
10069  }
10070  }
10071  }
10072  }
10073  }
10074  }
10075 
10076  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
10077  // iteration, next cycle will catch up with any other pending updates
10078  if(!TrainVector.empty())
10079  {
10080  TrainAdded = false;
10081 
10082 //elapsed time investigations
10083 
10084 //elapsed time segment
10085 //double Start, End;
10086 //AnsiString ElapsedTimeReport = "";
10087 //end elasped time segment
10088  AllRoutes->CallonVector.clear(); // this will be rebuilt during the calls to UpdateTrain
10089 //elapsed time segment
10090 //PerfLogForm->PerformanceLog(-1, "\n Train vector size: " + AnsiString(TrainVector.size()) + '\n');
10091 //PerfLogForm->PerformanceLog(-1, "Start time list");
10092 //end elapsed time segment
10093  for(unsigned int x = 0; x < TrainVector.size(); x++)
10094  {
10095 //elapsed time segment
10096 //Start = double(GetTime()) * 86400; //secs
10097 //end elapsed time segment
10098  TrainVectorAt(33, x).UpdateTrain(0);
10099 //elapsed time segment
10100 //End = double(GetTime()) * 86400;
10101 //ElapsedTimeReport = TrainVectorAt(-1, x).TrainDataEntryPtr->ServiceReference + AnsiString(" ") + AnsiString(int((End - Start) * 1000)); //msecs
10102 //PerfLogForm->PerformanceLog(-1, ElapsedTimeReport);
10103 //end elapsed time segment
10104 
10105 //end elapsed time investigations
10106 
10107  /* added HasTrainGone() condition below in v0.4c to prevent 2 trains both having TrainGone set in UpdateTrain
10108  at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
10109  iterates in reverse to erase the second train to have gone (when the first train to have gone comes before the second in TrainVector),
10110  but afterwards ReplotTrains iterates forwards and therefore replots the first train to have gone and therefore sets the TrainIDOnElement value
10111  to the exited train, with nothing to reset it. Hovering the mouse over that element with train information enabled causes an error because
10112  the track element thinks the train is still there, whereas it is missing from the TrainVector. BUT subsequently (in v2.11.1) changed RePlotTrains
10113  so it doesn't plot trains with TrainGone set, but left this is as does no harm
10114 
10115  Had another error notified by Kevin Smith on 02/01/22 where a train was manually removed in the same clock cycle as a train exited, and this caused
10116  the same error as above. Did a lot of experimenting but eventually cured it with two changes, first as above in RePlotTrains, and also below adding
10117  a break; command after one TrainHasGone() dealt with. There were introduced in v2.11.1 & seems ok now
10118 
10119  These changes should deal with any number of TrainGone flags set in the same clock cycle - from exiting, manual removal, or joins
10120  */
10121  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
10122  {
10123  break; //only one exited train will be dealt with at a time (see below) so no point looking further
10124  }
10125  }
10126  // set warning flags (ManualLCDownAttentionWarning dealt with in InterfaceUnit)
10127  CrashWarning = false;
10128  DerailWarning = false;
10129  SPADWarning = false;
10130  CallOnWarning = false;
10131  SignalStopWarning = false;
10132  BufferAttentionWarning = false;
10133  TrainFailedWarning = false;
10134  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
10135  {
10136  TTrain &Train = TrainVectorAt(34, x);
10137  if(Train.Crashed)
10138  // can't use background colours for crashed & derailed because same colour
10139  {
10140  CrashWarning = true;
10141  }
10142  else if(Train.Derailed)
10143  // can't use background colours for crashed & derailed because same colour
10144  {
10145  DerailWarning = true;
10146  }
10147  else if(Train.BackgroundColour == clSPADBackground)
10148  // use colour as that changes as soon as passes signal
10149  {
10150  SPADWarning = true;
10151  }
10152  else if(Train.BackgroundColour == clTrainFailedBackground)
10153  {
10154  TrainFailedWarning = true;
10155  }
10156  else if(Train.BackgroundColour == clCallOnBackground)
10157  // use colour as also stopped at signal
10158  {
10159  CallOnWarning = true;
10160  }
10161  else if(Train.BackgroundColour == clSignalStopBackground)
10162  // use colour to distinguish from call-on
10163  {
10164  SignalStopWarning = true;
10165  }
10166  else if(Train.BackgroundColour == clBufferAttentionNeeded)
10167  // use colour to distinguish from ordinary buffer stop
10168  {
10169  BufferAttentionWarning = true;
10170  }
10171  if(Train.HasTrainGone())
10172  {
10173  AnsiString Loc = "";
10174  bool ElementFound = false;
10175  TTrackElement TE;
10176  if(Train.LagElement > -1)
10177  {
10178  TE = Track->TrackElementAt(531, Train.LagElement);
10179  ElementFound = true;
10180  }
10181  else if(Train.MidElement > -1)
10182  {
10183  TE = Track->TrackElementAt(779, Train.MidElement);
10184  ElementFound = true;
10185  }
10186  else if(Train.LeadElement > -1)
10187  {
10188  TE = Track->TrackElementAt(780, Train.LeadElement);
10189  ElementFound = true;
10190  }
10191  if(ElementFound)
10192  {
10193  if(TE.ActiveTrackElementName != "")
10194  {
10195  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
10196  }
10197  else
10198  {
10199  Loc = "track element " + TE.ElementID;
10200  }
10201  }
10202  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
10203  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
10204  // need above first because may also have ActionVectorEntryPtr == "Fer"
10205  {
10206  Train.UnplotTrain(9);
10207  // added at v1.3.0 to reset signals after train removed from an autosigsroute
10209  {
10212  }
10213  // end of addition
10214  AllRoutes->RebuildRailwayFlag = true;
10215  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
10216  // correctly after a crash
10217  }
10218  else if(AVEntryPtr->Command == "Fer")
10219  {
10220  bool CorrectExit = false;
10221  if(!AVEntryPtr->ExitList.empty())
10222  {
10223  for(TNumListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
10224  {
10225  if(*ELIT == Train.LagElement)
10226  {
10227  CorrectExit = true;
10228  }
10229  }
10230  }
10231  if(CorrectExit)
10232  {
10233  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
10234  }
10235  else
10236  {
10237  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
10238  }
10239  }
10240  else
10241  {
10242  if(!AVEntryPtr->SignallerControl)
10243  {
10244  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
10245  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
10246  // -2 is marker for send messages for all remaining actions except Fer if present
10247  }
10248  else
10249  {
10250  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
10251  }
10252  }
10253  Utilities->CumulativeDelayedRandMinsAllTrains += Train.CumulativeDelayedRandMinsOneTrain; //added at v2.13.0 for random delays
10254  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
10255  Train.DeleteTrain(1);
10256  TrainVector.erase(TrainVector.begin() + x);
10257  ReplotTrains(1, Display); //to reset ElementIDs for remaining trains when have removed a train
10258  //NB: won't plot any trains with TrainGone flag set (changed at v2.11.1)
10259  break; //added at v2.11.1 to ensure that only one train with TrainGone set is dealt with in one clock cycle
10260  }
10261  }
10262  }
10263  else
10264  {
10265  // reset all flags in case last train removed with flag set
10266  CrashWarning = false;
10267  DerailWarning = false;
10268  SPADWarning = false;
10269  CallOnWarning = false;
10270  SignalStopWarning = false;
10271  BufferAttentionWarning = false;
10272  TrainFailedWarning = false;
10273  }
10274  // update OpTimeToActMultimap
10276  {
10278  // clears entries then adds values for running trains then for continuation entries
10280  //added for multiplayer for running trains only
10281  }
10282  Utilities->Clock2Stopped = ClockState;
10283  Utilities->CallLogPop(723);
10284 }
10285 
10286 // ---------------------------------------------------------------------------
10288 {
10289  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
10290  if(!TrainVector.empty())
10291  {
10292  for(int x = TrainVector.size() - 1; x >= 0; x--)
10293  {
10294  TrainVectorAt(50, x).DeleteTrain(2);
10295  }
10296  TrainVector.clear();
10297  }
10298  if(!TrainDataVector.empty())
10299  {
10300  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
10301  {
10302  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
10303  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
10304  {
10305  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
10306  TOD.RunningEntry = NotStarted;
10307  TOD.TrainID = -1;
10308  TOD.EventReported = NoEvent;
10309  }
10310  }
10311  }
10312  Display->GetOutputLog1()->Caption = "";
10313  Display->GetOutputLog2()->Caption = "";
10314  Display->GetOutputLog3()->Caption = "";
10315  Display->GetOutputLog4()->Caption = "";
10316  Display->GetOutputLog5()->Caption = "";
10317  Display->GetOutputLog6()->Caption = "";
10318  Display->GetOutputLog7()->Caption = "";
10319  Display->GetOutputLog8()->Caption = "";
10320  Display->GetOutputLog9()->Caption = "";
10321  Display->GetOutputLog10()->Caption = "";
10322  Utilities->CallLogPop(1352);
10323 }
10324 
10325 // ---------------------------------------------------------------------------
10326 
10328 {
10329  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
10330  if(!TrainVector.empty())
10331  {
10332  for(unsigned int x = 0; x < TrainVector.size(); x++)
10333  {
10334  if(!TrainVectorAt(84, x).HasTrainGone()) //added at v2.11.0 to prevent plotting a train pending removal & particularly to prevent TrainElementID's being reinstated
10335  { //see Kevin Smith error information for details
10336  TrainVectorAt(51, x).PlotTrain(4, Disp);
10337  }
10338  }
10339  }
10340  Utilities->CallLogPop(724);
10341 }
10342 
10343 // ---------------------------------------------------------------------------
10344 
10345 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
10346 {
10347  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
10348  if(!TrainVector.empty())
10349  {
10350  for(unsigned int x = 0; x < TrainVector.size(); x++)
10351  {
10352  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
10353  }
10354  }
10355  Utilities->CallLogPop(1707);
10356 }
10357 
10358 // ---------------------------------------------------------------------------
10359 
10361 {
10362  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
10363  if(!TrainVector.empty())
10364  {
10365  for(unsigned int x = 0; x < TrainVector.size(); x++)
10366  {
10367  TrainVectorAt(52, x).UnplotTrain(10);
10368  }
10369  }
10371  Utilities->CallLogPop(725);
10372 }
10373 
10374 // ---------------------------------------------------------------------------
10375 
10376 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
10377  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
10378  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
10379 {
10380  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
10381  "," + AnsiString(Mass) + "," + ModeStr);
10382  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
10383  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr); //at v2.11.1 dropped later headcode - was listed twice
10384 
10385  int RearExitPos = -1;
10386 
10387  for(int x = 0; x < 4; x++)
10388  {
10389  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
10390  {
10391  RearExitPos = x;
10392  }
10393  }
10394  if(RearExitPos == -1)
10395  {
10396  throw Exception("Error, RearExit == -1 in AddTrain");
10397  }
10398  bool ReportFlag = true;
10399 
10400  // used to stop repeated messages from CheckStartAllowable when split failed
10401  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
10402  {
10403  ReportFlag = false;
10404  }
10405  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
10406  {
10407  // messages sent to performance log in CheckStartAllowable if ReportFlag true
10408  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
10409  Utilities->CallLogPop(938);
10410  return(false);
10411  }
10412  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
10413  TTrainMode TrainMode = NoMode;
10414 
10415  if(ModeStr == "Timetable")
10416  {
10417  TrainMode = Timetable;
10418  }
10419  // all else gives 'None', 'Signaller' set within program
10420 
10421  if(MaxRunningSpeed < 10)
10422  {
10423  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
10424  }
10425  if(SignallerSpeed < 10)
10426  {
10427  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
10428  }
10429  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
10430  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
10431 
10432  LogEvent("AddTrainSupplemental: Service Ref = " + TrainDataEntryPtr->ServiceReference + ", TrainID = " + AnsiString(NewTrain->TrainID)); //new at v2.11.1 so can relate headcode to ID
10433 
10434  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
10435  // initialise here rather than in TTrain constructor as create trains
10436  // with Null TrainDataEntryPtr when loading session trains
10437  if(SignallerControl)
10438  {
10439  NewTrain->TimetableFinished = true;
10440  NewTrain->SignallerStoppingFlag = false;
10441  NewTrain->TrainMode = Signaller;
10442  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
10443  {
10444  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
10445  }
10447  }
10448  // deal with starting conditions:-
10449  // unlocated Snt: just report entry & advance pointer
10450  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
10451  // Sns doesn't need a new train
10452  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
10453  // covers all above located starts
10454  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
10455  // wouldn't have accepted the timetable
10456  {
10457  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
10458  // StoppedAtBuffers is set in UpdateTrain()
10459  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
10460  // buffer end must be ahead of train or would have failed start position check
10461  {
10462  NewTrain->StoppedAtLocation = true;
10463  NewTrain->PlotStartPosition(0);
10465  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
10466  NewTrain->ActionVectorEntryPtr->Warning);
10467  if(!SignallerControl) // don't advance if SignalControlEntry
10468  {
10469  NewTrain->ActionVectorEntryPtr++;
10470  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
10471  }
10472  NewTrain->LastActionTime = TTClockTime;
10473  }
10474  // else a through station stop
10475  else
10476  {
10477  NewTrain->StoppedAtLocation = true;
10478  NewTrain->PlotStartPosition(10);
10480  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
10481  NewTrain->ActionVectorEntryPtr->Warning);
10482  if(!SignallerControl) // don't advance if SignalControlEntry
10483  {
10484  NewTrain->ActionVectorEntryPtr++;
10485  }
10486  NewTrain->LastActionTime = TTClockTime;
10487  }
10488  }
10489  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
10490  {
10491  NewTrain->PlotStartPosition(11);
10492  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
10493  AnsiString Loc = "";
10494  if(TE.ActiveTrackElementName != "")
10495  {
10496  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
10497  }
10498  else
10499  {
10500  Loc = "track element " + TE.ElementID;
10501  }
10502  if(TE.TrackType == Continuation)
10503  {
10504  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
10505  }
10506  else
10507  {
10508  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
10509  }
10510  if(!SignallerControl) // don't advance if SignalControlEntry
10511  {
10512  NewTrain->ActionVectorEntryPtr++;
10513  }
10514  NewTrain->LastActionTime = TTClockTime;
10515  // no need to set LastActionTime for an unlocated entry
10516  }
10517  // cancel a wrong-direction route if either element of train starts on one
10518  if(NewTrain->LeadElement > -1)
10519  {
10520  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
10521  }
10522  if(NewTrain->MidElement > -1)
10523  {
10524  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
10525  }
10526  // set signals for a right-direction autosigs route for either element of train on one
10527  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
10528  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
10529  int RouteNumber = -1;
10530  bool SignalsSet = false;
10531 
10532  if(NewTrain->LeadElement > -1)
10533  {
10534  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
10535  {
10536  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
10537  int RouteStartPosition;
10538  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
10539  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
10540  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
10541  if(FirstPair.first == RouteNumber)
10542  {
10543  RouteStartPosition = FirstPair.second;
10544  }
10545  else if(SecondPair.first == RouteNumber)
10546  {
10547  RouteStartPosition = SecondPair.second;
10548  }
10549  else
10550  {
10551  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
10552  }
10553  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
10554  SignalsSet = true;
10555  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
10556  }
10557  else if(RouteNumber > -1) // non-autosigsroute
10558  {
10559  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
10560  int FirstTVPos = TempPDE.GetTrackVectorPosition();
10561  int FirstELinkPos = TempPDE.GetELinkPos();
10562  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
10563  {
10564  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10565  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
10566  }
10567  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
10568  {
10569  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10570  // remove the last element under LeadElement
10571  }
10572  AllRoutes->RebuildRailwayFlag = true;
10573  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
10574  // now deal with a rear linked autosigs route
10575  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
10576  {
10577  int LinkedRouteNumber = -1;
10578  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
10579  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
10580  {
10581  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
10582  // this is ok as here we are setting signals from the start of the route
10583  }
10584  }
10585  SignalsSet = true;
10586  }
10587  }
10588  if(NewTrain->MidElement > -1)
10589  // if entering at a continuation MidElement == -1
10590  {
10591  // this is included in case a train starts with LeadElement on no route and MidElement on a route
10592  if(!SignalsSet)
10593  {
10594  RouteNumber = -1;
10595  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
10596  {
10597  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
10598  int RouteStartPosition;
10599  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
10600  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
10601  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
10602  if(FirstPair.first == RouteNumber)
10603  {
10604  RouteStartPosition = FirstPair.second;
10605  }
10606  else if(SecondPair.first == RouteNumber)
10607  {
10608  RouteStartPosition = SecondPair.second;
10609  }
10610  else
10611  {
10612  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
10613  }
10614  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
10615  SignalsSet = true;
10616  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
10617  }
10618  else if(RouteNumber > -1) // non-autosigsroute
10619  {
10620  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
10621  int FirstTVPos = TempPDE.GetTrackVectorPosition();
10622  int FirstELinkPos = TempPDE.GetELinkPos();
10623  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
10624  {
10625  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10626  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
10627  }
10628  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
10629  {
10630  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10631  // remove the last element under LeadElement
10632  }
10633  AllRoutes->RebuildRailwayFlag = true;
10634  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
10635  // now deal with a rear linked autosigs route
10636  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
10637  {
10638  int LinkedRouteNumber = -1;
10639  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
10640  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
10641  {
10642  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
10643  // this is ok as now we are setting signals from the start of the route
10644  }
10645  }
10646  }
10647  }
10648  }
10649  TrainVector.push_back(*NewTrain);
10650  Utilities->CallLogPop(731);
10651  return(true);
10652 }
10653 
10654 // ---------------------------------------------------------------------------
10655 
10656 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
10657 {
10658  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
10659  AnsiString(TrackVectorNumber));
10660  int VecPos = -1;
10661 
10662  for(unsigned int x = 0; x < TrainVector.size(); x++)
10663  {
10664  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
10665  {
10666  VecPos = x;
10667  }
10668  }
10669  if(VecPos == -1)
10670  {
10671  throw Exception("Error, VecPos not set in EntryPos");
10672  }
10673  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
10674  {
10675  Utilities->CallLogPop(734);
10676  return(TrainVectorAt(3, VecPos).LeadEntryPos);
10677  }
10678  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
10679  {
10680  Utilities->CallLogPop(735);
10681  return(TrainVectorAt(5, VecPos).MidEntryPos);
10682  }
10683  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
10684  {
10685  Utilities->CallLogPop(736);
10686  return(TrainVectorAt(7, VecPos).LagEntryPos);
10687  }
10688  Utilities->CallLogPop(737);
10689  return(-1);
10690 }
10691 
10692 // ---------------------------------------------------------------------------
10693 
10695 {
10696  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
10697  for(unsigned int x = 0; x < TrainVector.size(); x++)
10698  {
10699  if(TrainVectorAt(53, x).TrainID == TrainID)
10700  {
10701  Utilities->CallLogPop(738);
10702  return(TrainVectorAt(54, x));
10703  }
10704  }
10705  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
10706 }
10707 
10708 // ---------------------------------------------------------------------------
10709 
10710 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
10711 // return true if find the train (added at v2.4.0 as can select a removed train in
10712 // ActionsDueListBox before it updates - reported by LiWinDom in error report 23/04/20)
10713 {
10714  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
10715  for(unsigned int x = 0; x < TrainVector.size(); x++)
10716  {
10717  if(TrainVectorAt(69, x).TrainID == TrainID)
10718  {
10719  Utilities->CallLogPop(2152);
10720  return(true);
10721  }
10722  }
10723  Utilities->CallLogPop(2153);
10724  return(false);
10725 }
10726 
10727 // ---------------------------------------------------------------------------
10728 
10729 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
10730 {
10731  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
10732  Utilities->Format96HHMMSS(Time));
10733  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
10734 
10735  Utilities->CallLogPop(2061);
10736  return(RepeatTime);
10737 }
10738 
10739 // ---------------------------------------------------------------------------
10740 
10741 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
10742 // Enter with Ptr pointing to first action to be listed (i.e. next action)
10743 {
10744  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
10745  AnsiString RetStr = "", PartStr = "";
10746  int Count = 0;
10747  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
10748 
10749  Ptr--; // because incremented at start of loop
10750  do
10751  {
10752  Ptr++;
10753  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
10754  {
10755  continue; // move past the starting entry
10756  }
10757  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
10758  {
10759  break;
10760  }
10761  if(Ptr->SignallerControl)
10762  {
10763  RetStr = "Train under signaller control";
10764  break;
10765  }
10766  if(Ptr->FormatType == TimeTimeLoc)
10767  {
10768  if(Ptr->ArrivalTime == Ptr->DepartureTime)
10769  {
10770  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
10771  }
10772  else
10773  {
10774  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
10775  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10776  Count++; // because there are 2 entries
10777  }
10778  }
10779  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
10780  {
10781  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
10782  }
10783  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
10784  {
10785  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10786  }
10787  else if(Ptr->FormatType == PassTime) // new
10788  {
10789  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
10790  }
10791  else if(Ptr->Command == "Fns")
10792  {
10793  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10794  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10795  PartStr = ControllerGetNewServiceDepartureInfo(11, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
10796  }
10797  else if(Ptr->Command == "F-nshs")
10798  {
10799  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10800  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
10801  PartStr = ControllerGetNewServiceDepartureInfo(13, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10802  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
10803  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
10804  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
10805  }
10806 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
10807  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10808  {
10809  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10810  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10811  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10812  PartStr = ControllerGetNewServiceDepartureInfo(15, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10813  }
10814  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10815  {
10816  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10817  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
10818  PartStr = ControllerGetNewServiceDepartureInfo(17, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10819  }
10820  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10821  {
10822  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10823  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10824  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10825  PartStr = ControllerGetNewServiceDepartureInfo(19, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10826  }
10827  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10828  {
10829  PartStr = "Terminate at " + Ptr->LocationName;
10830  }
10831  else if(Ptr->Command == "Frh")
10832  {
10833  PartStr = "Terminate at " + Ptr->LocationName;
10834  }
10835  else if(Ptr->Command == "Fer")
10836  {
10837  AnsiString AllowedExits;
10838  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
10839  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
10840  }
10841  else if(Ptr->Command == "Fjo")
10842  {
10843  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
10844  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10845  }
10846  else if(Ptr->Command == "jbo")
10847  {
10848  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
10849  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10850  }
10851  else if(Ptr->Command == "fsp")
10852  {
10853  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
10854  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10855  }
10856  else if(Ptr->Command == "rsp")
10857  {
10858  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
10859  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10860  }
10861  else if(Ptr->Command == "cdt")
10862  {
10863  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
10864  }
10865  else if(Ptr->Command == "dsc")
10866  {
10867  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(27, Ptr->EventTime, RepNum, IncMins)) + ": Change description at " + Ptr->LocationName;
10868  }
10869  if(RetStr != "")
10870  {
10871  RetStr = RetStr + '\n' + PartStr;
10872  }
10873  else
10874  {
10875  RetStr = PartStr;
10876  }
10877  Count++;
10878  }
10879  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
10880  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
10881  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
10882  // forward as anyone should wish to see without looking at the full timetable
10883  Utilities->CallLogPop(2072);
10884  return(RetStr);
10885 }
10886 
10887 // ---------------------------------------------------------------------------
10888 
10889 AnsiString TTrainController::ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
10890 { //no delays as train not entered yet
10891  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - TDEPtr->ActionVector.begin()) + ","
10892  + AnsiString(RptNum) + ",ControllerGetNewServiceDepartureInfo," + TDEPtr->HeadCode);
10893  AnsiString DepTime = "", EventTime = "";
10894  bool CDTFlag = false; //reports if train changes direction before departs
10895  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
10896  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
10897  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
10898  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
10899  {
10900  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
10901  {
10902  TowardsLocation = AVI->LocationName;
10903  }
10904  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
10905  {
10906  TTrackElement TE = Track->TrackElementAt(1453, (AVI->ExitList.front()));
10907  if(TE.ActiveTrackElementName != "")
10908  {
10909  TowardsLocation = TE.ActiveTrackElementName;
10910  }
10911  else
10912  {
10913  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
10914  }
10915  }
10916  }
10917  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
10918  {
10919  if(AVI->Command == "cdt")
10920  {
10921  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
10922  continue;
10923  }
10924  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
10925  {
10926  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
10927  RetStr += "\nNew service splits at " + EventTime;
10928  Utilities->CallLogPop(2237);
10929  return(RetStr);
10930  }
10931  if(AVI->Command == "jbo")
10932  {
10933  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(30, AVI->EventTime, RptNum, IncrementalMinutes));
10934  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
10935  Utilities->CallLogPop(2238);
10936  return(RetStr);
10937  }
10938  if((AVI->Command == "Fns") || (AVI->Command == "F-nshs") || (AVI->Command == "Fns-sh"))
10939  {
10940  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(31, AVI->EventTime, RptNum, IncrementalMinutes));
10941  RetStr += "\nNew service finishes and forms another new service at " + EventTime;
10942  Utilities->CallLogPop(2607);
10943  return(RetStr);
10944  }
10945  if(AVI->Command == "Fjo")
10946  {
10947  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
10948  RetStr += "\nNew service finishes and joins " + AVI->OtherHeadCode + " at " + EventTime;
10949  Utilities->CallLogPop(2608);
10950  return(RetStr);
10951  }
10952  if(AVI->Command == "Frh")
10953  {
10954  RetStr += "\nNew service finishes and remains at location.";
10955  Utilities->CallLogPop(2609);
10956  return(RetStr);
10957  }
10958  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
10959  {
10960  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
10961  if(CDTFlag)
10962  {
10963  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10964  {
10965  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
10966  }
10967  else
10968  {
10969  RetStr += "\nNew service changes direction then departs at " + DepTime;
10970  }
10971  }
10972  else
10973  {
10974  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10975  {
10976  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
10977  }
10978  else
10979  {
10980  RetStr += "\nNew service departs at " + DepTime;
10981  }
10982  }
10983  Utilities->CallLogPop(2239);
10984  return(RetStr);
10985  }
10986  }
10987  Utilities->CallLogPop(2223);
10988  return(RetStr);
10989 }
10990 
10991 // ---------------------------------------------------------------------------
10992 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
10993 /*
10994  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
10995  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
10996  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
10997 
10998  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
10999  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
11000  user wishes
11001 
11002  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
11003  descriptive text or anything user wishes
11004  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
11005  be ignored) is taken as the timetable start time.
11006  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
11007  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
11008  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
11009  within the timetable if required.
11010  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
11011  services)
11012  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
11013  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
11014 
11015  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
11016  text timetable file easier
11017 
11018  form:-
11019  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
11020  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
11021  then multiple entries, separated by commas, of the form:-
11022 
11023  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
11024  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
11025  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
11026 
11027  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
11028  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
11029  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
11030 
11031  HH:MM;Command (cdt) }TimeCmd }
11032  HH:MM;Command;Description (dsc) }TimeCmdDescription }
11033  HH:MM;Location (arr & dep) }TimeLoc }
11034  HH:MM;HH:MM;Location }TimeTimeLoc }
11035  HH:MM;pas;Location }PassTime }
11036  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
11037  HH:MM;Fer;set of allowable IDs }ExitRailway }
11038  Command (Frh only) }FinRemHere }
11039 
11040  R;mm;dd;nn. Repeat Repeat entry
11041 
11042  Formats:
11043 
11044  Command only: Frh
11045  Time;Command: cdt
11046  Time;Command;Description dsc
11047  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
11048  Time;Command;2 Element IDs: Snt
11049  Time;Comand;n Element IDs: Fer
11050  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
11051  Time;Command;2 Element IDs;Headcode Snt-sh
11052  Time;Command;Location pas
11053  Time;Location Arr Dep
11054  Time;Time;Location Arr & dep together
11055 
11056  10 Non-linked entries: Snt (located or unlocated); pas; cdt; dsc; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
11057 
11058  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
11059  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
11060  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
11061 
11062  4 2x Linked entries, all shuttles:
11063 
11064  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
11065  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
11066  -> Remain Here (at finish location after all repeats)
11067  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
11068  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
11069 
11070  Moving/AtLoc states:-
11071 
11072  Successor state Type
11073 
11074  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/dsc/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
11075  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
11076  Sfs AtLoc )
11077  Sns AtLoc ) Start
11078  Sns-fsh AtLoc )
11079  Snt-sh AtLoc )
11080  Sns-sh AtLoc )
11081 
11082  pas Moving )
11083  jbo AtLoc )
11084  fsp AtLoc )
11085  rsp AtLoc ) Intermediate
11086  cdt AtLoc )
11087  dsc AtLoc )
11088  TimeLoc arr Moving (bef), AtLoc (aft) )
11089  TimeLoc dep AtLoc (bef), Moving (aft) )
11090  TimeTimeLoc Moving )
11091 
11092  Fns Repeat/Nothing)
11093  Fjo Repeat/Nothing)
11094  Frh Repeat/Nothing)
11095  Fer Repeat/Nothing) Finish
11096  Frh-sh Repeat )
11097  Fns-sh Repeat )
11098  F-nshs Nothing )
11099 
11100  Descriptions:
11101  Snt New train
11102  Sfs New service from split
11103  Sns New service from another service
11104  Sns-fsh New non-repeating service from a shuttle service
11105  Snt-sh New shuttle train at a timetabled stop
11106  Sns-sh New shuttle service from a feeder service
11107 
11108  pas Pass
11109  jbo Be joined by another train
11110  fsp Front split
11111  rsp Rear split
11112  cdt Change direction of train
11113  dsc Change description of train
11114  TimeLoc arr Arrival
11115  TimeLoc dep Departure
11116  TimeTimeLoc Arrival and departure
11117 
11118  Fns Finish & form a new service
11119  Fjo Finish & join another train
11120  Frh Finish & remain here
11121  Fer Finish & exit railway
11122  Frh-sh Finish & repeat shuttle, finally remain here
11123  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
11124  F-nshs Finish & form a shuttle feeder service
11125 */
11126 
11127 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
11128 {
11129  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
11130  // a line that is too long; timetable containing too few lines; and timetable failed to open.
11131  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
11132  // new for v0.2b
11133  // compile ActiveTrackElementNameMap
11134  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
11135 
11137  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
11138  {
11139  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
11141  == Track->ContinuationNameMap.end())
11142  {
11143  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
11144  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
11145  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
11146  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
11147  }
11148  }
11150  // end of new section
11151  std::ifstream TTBLFile(FileName, std::ios_base::binary);
11152 
11153  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
11154  if(TTBLFile.is_open())
11155  {
11156  char *TrainTimetableString = new char[10000];
11157  // enough for over 200 stations, should be adequate!
11158  bool EndOfFile = false;
11159  int Count = 0;
11160  // counts 'relevant' lines, i.e ignores any before the start time on its own line
11161  TTBLFile.getline(TrainTimetableString, 10000, '\0');
11162  // delimiter is '\0' as it's an AnsiString
11163  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
11164  // file empty - stores a null in 1st position if doesn't load any characters
11165  {
11166  // may still have eof even if read a line (no CRLF at end), and
11167  // if so need to process it
11168  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
11169  TTBLFile.close();
11170  delete[] TrainTimetableString;
11171  Utilities->CallLogPop(1611);
11172  return(false);
11173  }
11174  AnsiString OneLine(TrainTimetableString);
11175  bool FinalCallFalse = false;
11176  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
11177  // get rid of lines before the start time
11178  {
11179  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
11180  TTBLFile.getline(TrainTimetableString, 10000, '\0');
11181  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
11182  // stores a null in 1st position if doesn't load any characters
11183  {
11184  // may still have eof even if read a line (no CRLF at end), and
11185  // if so need to process it
11186  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
11187  TTBLFile.close();
11188  delete[] TrainTimetableString;
11189  Utilities->CallLogPop(772);
11190  return(false);
11191  }
11192  OneLine = AnsiString(TrainTimetableString);
11193  }
11194  // here when have accepted the start time
11195  Count++; // increment past the start time
11196  while(!EndOfFile)
11197  {
11198  TTBLFile.getline(TrainTimetableString, 10000, '\0');
11199  // get next line after start time
11200  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
11201  // stores a null in 1st position if doesn't load any characters
11202  {
11203  // may still have eof even if read a line (no CRLF at end), and
11204  // if so need to process it
11205  EndOfFile = true;
11206  OneLine = "";
11207  }
11208  else
11209  {
11210  OneLine = AnsiString(TrainTimetableString);
11211  }
11212  if(OneLine.Length() > 9999)
11213  {
11214  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
11215  TTBLFile.close();
11216  delete[] TrainTimetableString;
11217  Utilities->CallLogPop(789);
11218  return(false);
11219  }
11220  bool FinalCallFalse = false;
11221  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
11222  // false for FinalCall - just checking at this stage
11223  {
11224  TTBLFile.close();
11225  delete[] TrainTimetableString;
11226  Utilities->CallLogPop(770);
11227  return(false);
11228  }
11229  if(EndOfFile && (Count < 2))
11230  // Timetable must contain at least two relevant lines, one for start time and at least one train
11231  {
11232  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
11233  TTBLFile.close();
11234  delete[] TrainTimetableString;
11235  Utilities->CallLogPop(771);
11236  return(false);
11237  }
11238  Count++;
11239  }
11240  delete[] TrainTimetableString;
11241  TTBLFile.close();
11242  } // if(TTBLFile.is_open())
11243  else
11244  {
11245  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
11246  Utilities->CallLogPop(2154);
11247  return(false);
11248  }
11249  Utilities->CallLogPop(753);
11250  return(true);
11251 }
11252 
11253 // ---------------------------------------------------------------------------
11254 
11255 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
11256  bool CheckLocationsExistInRailway) // return true for success
11257 
11258 /* Format:
11259  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
11260  descriptive text or anything user wishes
11261  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
11262  be ignored) is taken as the timetable start time.
11263  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
11264  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
11265  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
11266  within the timetable if required.
11267  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
11268  services)
11269  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
11270  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
11271 
11272  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
11273  text timetable file easier
11274 
11275  form:-
11276  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
11277  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
11278  then multiple entries, separated by commas, of the form:-
11279 
11280  Format FormatType
11281  [W]HH:MM;Command (cdt) }TimeCmd }
11282  [W]HH:MM;dsc;new description }TimeCmdDescription }
11283  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
11284  [W]HH:MM;pas;Location }PassTime }
11285  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
11286  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
11287  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
11288  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
11289  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
11290  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
11291  [W]HH:MM;Fns-sh;Details }FSHNewService }
11292  [W]HH:MM;Location (arr & dep) }TimeLoc }
11293  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
11294  Command (Frh only) }FinRemHere }
11295 
11296  R;mm;dd;nn. Repeat Repeat entry
11297 
11298  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
11299  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
11300  at location; or (c) departure time if train already at location (including train started at location either as a new
11301  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
11302  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
11303  minutes, incremental train headcode last 2 digits, and number of repeats.
11304 
11305  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
11306  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
11307  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
11308  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
11309  (it's for a shuttle train to return to depot at end of services)
11310 
11311  Command/Location & details are as follows:-
11312 
11313  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
11314  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
11315  2E44 in its Sfs entry. All these are checked.
11316  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
11317 
11318  Start commands:-
11319  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
11320  with loc as a start entry can't have a location as details)
11321  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
11322  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
11323  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
11324  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
11325  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
11326  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
11327  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
11328  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
11329  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
11330  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
11331  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
11332 
11333  Intermediate commands:-
11334  Time - Location (TimeLoc), can be arrival or departure depending on context
11335  Time Time location (TimeTimeLoc), arrival and departure
11336  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
11337  pas (PassTime), Time;pas;Location
11338  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
11339  joining train's finish details must correspond or the file check will fail
11340  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
11341  new train - that train's starting information must correspond)
11342  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
11343  new train - that train's starting information must correspond)
11344  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
11345  dsc (TimeCmdDescription) = Change description of train = new description only, no train creation
11346 
11347  Finish commands:-
11348  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
11349  creation)
11350  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
11351  shuttle headcode (no train creation)
11352  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
11353  may have to wait for it), details = new headcode (delete train)
11354  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
11355  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
11356  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
11357  here
11358  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
11359  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
11360 
11361  Repeat:-
11362  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
11363  headcodes - it is up to user to avoid duplicates if he/she wishes to.
11364 
11365  Checks carried out with error messages in this function:-
11366  At least one comma in a service line (it's based on a .csv file)
11367  No entries following train information;
11368  At least one comma in remainder after train information (i.e at least a start and a finish entry);
11369  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
11370  First entry not a start entry;
11371  Train information incomplete before a start entry;
11372  Entry follows a finish entry but doesn't begin with 'R';
11373  SplitEntry returns false in a finish entry - message repeats the entry for information;
11374  Last action entry isn't a finish entry.
11375 
11376  Function returns false with no message if:-
11377  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
11378  time is found at all then an error message is given in the calling function);
11379  SplitTrainInfo returns false (message given in called function);
11380  SplitRepeat returns false (message given in called function).
11381 */
11382 {
11383  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
11384  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
11385  TTrainDataEntry TempTrainDataEntry;
11386 
11387  EndOfFile = false;
11388  StripSpaces(0, OneLine);
11389  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
11390  // semicolons within the line
11391  ServiceReference = "";
11392  if(OneLine != "")
11393  {
11394  if(OneLine[1] != '*')
11395  {
11396  int SCPos = OneLine.Pos(';');
11397  if(SCPos == 0)
11398  {
11399  ServiceReference = OneLine.SubString(1, 8);
11400  }
11401  else
11402  {
11403  ServiceReference = OneLine.SubString(1, (SCPos - 1));
11404  }
11405  }
11406  }
11407  bool AllCommas = true;
11408 
11409  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
11410  {
11411  if(OneLine[x] != ',')
11412  {
11413  AllCommas = false;
11414  }
11415  }
11416  if(AllCommas || (OneLine == ""))
11417  {
11418  if(Count > 0)
11419  {
11420  EndOfFile = true;
11421  // returns true for a blank line - treated as end of file
11422  Utilities->CallLogPop(1018);
11423  return(true);
11424  }
11425  else // count == 0 so not yet found a start time, no message to be given
11426  {
11427  Utilities->CallLogPop(754);
11428  return(false);
11429  }
11430  }
11431  AnsiString First = "", Second = "", Third = "", Fourth = "";
11432  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
11433  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
11434  TDateTime StartTime(0);
11435  TNumList ExitList;
11436  bool Warning = false;
11437 
11438  if(Count == 0) // no start time found yet
11439  {
11440 /* dropped at v0.6b
11441  AnyHeadCodeValid = false;
11442  if(OneLine.SubString(6,5) == ";0000")
11443  {
11444  AnyHeadCodeValid = true;
11445  }
11446 */
11447  if(!CheckTimeValidity(0, OneLine, StartTime))
11448  {
11449  // no message is given for an invalid time as it's assumed to be an irrelevant line
11450  // if no start time is found at all then an error message is given in the calling function
11451  // AnyHeadCodeValid = false;
11452  Utilities->CallLogPop(755);
11453  return(false);
11454  }
11455  if(FinalCall) // here if start time valid
11456  {
11457  TTClockTime = StartTime;
11458  TimetableStartTime = StartTime;
11459  }
11460  }
11461  else
11462  {
11463  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
11464  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
11465  double MaxBrakeRate = 0;
11466  double PowerAtRail = 0;
11467  int SignallerSpeed = 0;
11468  if(OneLine[1] == '*')
11469  {
11470  Utilities->CallLogPop(1581);
11471  return(true);
11472  // ignore any line beginning with '*' but return true as there is no error
11473  }
11474  int Pos = OneLine.Pos(',');
11475  if(Pos == 0)
11476  {
11477  int SubStringLength = 20;
11478  if(OneLine.Length() < 20)
11479  {
11480  SubStringLength = OneLine.Length();
11481  }
11482  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
11483  Utilities->CallLogPop(766);
11484  return(false);
11485  }
11486  TrainInfoStr = OneLine.SubString(1, Pos - 1);
11487  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
11488  GiveMessages)) // error messages given in SplitTrainInfo
11489  {
11490  Utilities->CallLogPop(773);
11491  return(false);
11492  }
11493  if(FinalCall)
11494  {
11495  // store Train info - conversions done in SplitTrainInfo
11496  // only headcode mandatory for continuing services
11497  //HeadCode = ServiceReference until final section of SecondPassActions
11498  TempTrainDataEntry.HeadCode = HeadCode;
11499  TempTrainDataEntry.ServiceReference = HeadCode;
11500  TempTrainDataEntry.Description = Description;
11501  TempTrainDataEntry.ExplicitDescription = false;
11502  if(Description != "")
11503  {
11504  TempTrainDataEntry.ExplicitDescription = true;
11505  }
11506  TempTrainDataEntry.StartSpeed = StartSpeed;
11507  TempTrainDataEntry.Mass = Mass;
11508  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
11509  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
11510  TempTrainDataEntry.PowerAtRail = PowerAtRail;
11511  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
11512  TTrainOperatingData TempTrainOperatingData;
11513  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
11514  }
11515  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
11516  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
11517  // so strip them off
11518  while(NewRemainder[NewRemainder.Length()] == ',')
11519  {
11520  if(NewRemainder.Length() > 1)
11521  {
11522  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
11523  }
11524  else
11525  {
11526  NewRemainder = "";
11527  break;
11528  }
11529  }
11530  // check if zero length & fail if so
11531  if(NewRemainder == "")
11532  {
11533  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
11534  Utilities->CallLogPop(769);
11535  return(false);
11536  }
11537  // now have one more entry than there are commas
11538  int CommaCount = 0;
11539  for(int x = 1; x < NewRemainder.Length() + 1; x++)
11540  {
11541  if(NewRemainder[x] == ',')
11542  {
11543  CommaCount++;
11544  }
11545  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
11546  if(CommaCount == 0)
11547  {
11548  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
11549  {
11550  int SubStringLength = 20;
11551  if(OneLine.Length() < 20)
11552  {
11553  SubStringLength = OneLine.Length();
11554  }
11555  TimetableMessage(GiveMessages,
11556  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
11557  OneLine.SubString(1, SubStringLength) + "'....");
11558  Utilities->CallLogPop(783);
11559  return(false);
11560  }
11561  }
11562  AnsiString OneEntry = "";
11563  TTimetableFormatType FormatType;
11564  TTimetableSequenceType SequenceType;
11565  TTimetableLocationType LocationType;
11566  TTimetableShuttleLinkType ShuttleLinkType;
11567  bool FinishFlag = false;
11568  bool NewTrain = false;//added at v2.14.0 to record created trains for later zero power checks
11569  for(int x = 0; x < CommaCount + 1; x++)
11570  {
11571  if((CommaCount == 0) || (x < CommaCount))
11572  // i.e. train entered under signaller control with no repeats, or entry is not the last,
11573  // in which case there's a comma & finish element or repeat still to come this entry could
11574  // be a finish but can't be a repeat
11575  {
11576  if(CommaCount == 0)
11577  {
11578  OneEntry = NewRemainder;
11579  NewRemainder = "";
11580  }
11581  else
11582  {
11583  Pos = NewRemainder.Pos(',');
11584  OneEntry = NewRemainder.SubString(1, Pos - 1);
11585  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
11586  }
11587  First = "";
11588  Second = "";
11589  Third = "";
11590  Fourth = "";
11591  RearStartOrRepeatMins = 0;
11592  FrontStartOrRepeatDigits = 0;
11593  NumberOfRepeats = 0;
11594  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11595  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11596  {
11597  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
11598  Utilities->CallLogPop(756);
11599  return(false);
11600  }
11601  if((Second == "Snt") || (Second == "Snt-sh")) //added at v2.14.0, see above
11602  {
11603  NewTrain = true;
11604  }
11605  // check if warning for Frh or Fjo & reject
11606  if(Warning && (Second == "Frh"))
11607  {
11608  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
11609  Utilities->CallLogPop(1793);
11610  return(false);
11611  }
11612  if(Warning && (Second == "Fjo"))
11613  {
11614  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11615  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
11616  Utilities->CallLogPop(1794);
11617  return(false);
11618  }
11619  //below added at v2.14.0 to prevent unpowered trains attempting to be joined by (Second == jbo), split (Second -- fsp or rsp),
11620  //or change direction. Form a new service dealt with below for zero power as it's a finish event.
11621  if(NewTrain && (PowerAtRail < 1) && (Second == "jbo"))
11622  {
11623  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11624  "': a train created without power can't 'be joined by' another train (i.e. can't include command 'jbo'), "
11625  "use command 'Fjo' (i.e. 'join' another train) instead immediately after the line containing 'Snt', and use "
11626  "command 'jbo' for the train it is to join.");
11627  Utilities->CallLogPop(2545);
11628  return(false);
11629  }
11630  if(NewTrain && (PowerAtRail < 1) && ((Second == "fsp") || (Second == "rsp")))
11631  {
11632  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11633  "': a train created without power can't split.");
11634  Utilities->CallLogPop(2546);
11635  return(false);
11636  }
11637  if(NewTrain && (PowerAtRail < 1) && (Second == "cdt"))
11638  {
11639  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11640  "': a train created without power can't change direction under timetable control.");
11641  Utilities->CallLogPop(2547);
11642  return(false);
11643  }
11644  //end of new additions
11645  if(x == 0) // should be start event
11646  {
11647  if(SequenceType != StartSequence)
11648  {
11649  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
11650  Utilities->CallLogPop(784);
11651  return(false);
11652  }
11653  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
11654  {
11655  if(NewRemainder[1] != 'R')
11656  {
11657  TimetableMessage(GiveMessages,
11658  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
11659  OneEntry + "'");
11660  Utilities->CallLogPop(787);
11661  return(false);
11662  }
11663  }
11664  if((Second == "Snt") || (Second == "Snt-sh"))
11665  // need full train information including non-default values for at least HeadCode, Description,
11666  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
11667  {
11668  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
11669  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
11670  {
11671  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
11672  OneEntry + "'");
11673  Utilities->CallLogPop(1783);
11674  return(false);
11675  }
11676  }
11677  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
11678  // service continuation - need at least non-default value for HeadCode
11679  {
11680  if(HeadCode == "")
11681  {
11682  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
11683  OneEntry + "'");
11684  Utilities->CallLogPop(788);
11685  return(false);
11686  }
11687  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
11688  {
11689  TimetableMessage(GiveMessages,
11690  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
11691  OneEntry + "'");
11692  Utilities->CallLogPop(843);
11693  return(false);
11694  }
11695  }
11696  }
11697  if(SequenceType == FinishSequence)
11698  {
11699  FinishFlag = true;
11700  // marker for only permitted additional entry being a repeat, only needed if the
11701  // finish entry is not the last entry
11702  }
11703  if(FinalCall)
11704  {
11705  // interpret & add to ActionVector
11706  TDateTime TempTime;
11707  TActionVectorEntry ActionVectorEntry;
11708  ActionVectorEntry.FormatType = FormatType;
11709  ActionVectorEntry.LocationType = LocationType;
11710  ActionVectorEntry.SequenceType = SequenceType;
11711  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11712  ActionVectorEntry.Warning = Warning;
11713  if(FormatType == TimeLoc)
11714  {
11715  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
11716  {
11717  ;
11718  } // these will all be true as final call
11719 
11720  ActionVectorEntry.LocationName = Second;
11721  }
11722  else if(FormatType == PassTime) // new
11723  {
11724  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
11725  {
11726  ;
11727  }
11728  ActionVectorEntry.Command = Second;
11729  ActionVectorEntry.LocationName = Third;
11730  }
11731  else if(FormatType == TimeTimeLoc)
11732  {
11733  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
11734  {
11735  ;
11736  }
11737  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
11738  {
11739  ;
11740  }
11741  ActionVectorEntry.LocationName = Third;
11742  }
11743  else if(FormatType == TimeCmd)
11744  {
11745  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
11746  {
11747  ;
11748  }
11749  ActionVectorEntry.Command = Second;
11750  }
11751  else if(FormatType == ExitRailway)
11752  {
11753  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
11754  {
11755  ;
11756  }
11757  ActionVectorEntry.Command = Second;
11758  ActionVectorEntry.ExitList = ExitList;
11759  }
11760  else if(FormatType == StartNew)
11761  {
11762  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
11763  {
11764  ;
11765  }
11766  ActionVectorEntry.Command = Second;
11767  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11768  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11769  if(Fourth == 'S')
11770  {
11771  ActionVectorEntry.SignallerControl = true;
11772  }
11773  }
11774  else if(FormatType == SNTShuttle)
11775  {
11776  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
11777  {
11778  ;
11779  }
11780  ActionVectorEntry.Command = Second;
11781  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11782  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11783  ActionVectorEntry.OtherHeadCode = Fourth;
11784  }
11785  else if(FormatType == SNSShuttle)
11786  {
11787  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
11788  {
11789  ;
11790  }
11791  ActionVectorEntry.Command = Second;
11792  ActionVectorEntry.OtherHeadCode = Third;
11793  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11794  }
11795  else if(FormatType == TimeCmdHeadCode) //fsp/rsp is TimeCmdHeadCode but may have a Fourth - see below
11796  {
11797  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
11798  {
11799  ;
11800  }
11801  ActionVectorEntry.Command = Second;
11802  ActionVectorEntry.OtherHeadCode = Third;
11803  if((Fourth != "") && ((Second == "fsp") || (Second == "rsp"))) //new at v2.15.0 & checked in SplitEntry
11804  {
11805  ActionVectorEntry.SplitDistribution = Fourth;
11806  }
11807  }
11808  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
11809  {
11810  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
11811  {
11812  ;
11813  }
11814  ActionVectorEntry.Command = Second;
11815  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11816  }
11817  else if(FormatType == FSHNewService)
11818  {
11819  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
11820  {
11821  ;
11822  }
11823  ActionVectorEntry.Command = Second;
11824  ActionVectorEntry.OtherHeadCode = Third;
11825  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11826  }
11827  else if(FormatType == FinRemHere)
11828  {
11829  ActionVectorEntry.Command = Second;
11830  }
11831  else if(FormatType == TimeCmdDescription) //new at v2.15.0
11832  {
11833  if(CheckTimeValidity(35, First, ActionVectorEntry.EventTime))
11834  {
11835  ;
11836  }
11837  ActionVectorEntry.Command = Second;
11838  ActionVectorEntry.NewDescription = Third;
11839  }
11840  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11841  }
11842  }
11843  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
11844  {
11845  OneEntry = NewRemainder;
11846  First = "";
11847  Second = "";
11848  Third = "";
11849  Fourth = "";
11850  RearStartOrRepeatMins = 0;
11851  FrontStartOrRepeatDigits = 0;
11852  NumberOfRepeats = 0;
11853  if((FinishFlag) && (OneEntry[1] != 'R'))
11854  // already had a finish entry
11855  {
11856  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
11857  Utilities->CallLogPop(79);
11858  return(false);
11859  }
11860  if(OneEntry[1] != 'R') // must be finish
11861  {
11862  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11863  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11864  {
11865  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
11866  Utilities->CallLogPop(757);
11867  return(false);
11868  }
11869  //below added at v2.14.0 to prevent unpowered trains attempting to form a new service.
11870  if(NewTrain && (PowerAtRail < 1) && ((Second == "Fns") || (Second == "Frh-sh") || (Second == "Fns-sh") || (Second == "F-nshs")))
11871  {
11872  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11873  "': a train created without power can't form a new service.");
11874  Utilities->CallLogPop(2548);
11875  return(false);
11876  }
11877  //end of new additions
11878  if(SequenceType != FinishSequence)
11879  {
11880  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
11881  Utilities->CallLogPop(785);
11882  return(false);
11883  }
11884  if(FinalCall)
11885  {
11886  // interpret & add to ActionVector
11887  TDateTime TempTime;
11888  TActionVectorEntry ActionVectorEntry;
11889  ActionVectorEntry.FormatType = FormatType;
11890  ActionVectorEntry.LocationType = LocationType;
11891  ActionVectorEntry.SequenceType = SequenceType;
11892  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11893  ActionVectorEntry.Warning = Warning;
11894  if(FormatType == TimeCmd)
11895  {
11896  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
11897  {
11898  ;
11899  }
11900  ActionVectorEntry.Command = Second;
11901  }
11902  else if(FormatType == TimeCmdHeadCode)
11903  {
11904  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
11905  {
11906  ;
11907  }
11908  ActionVectorEntry.Command = Second;
11909  ActionVectorEntry.OtherHeadCode = Third;
11910  }
11911  else if(FormatType == FNSNonRepeatToShuttle)
11912  {
11913  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
11914  {
11915  ;
11916  }
11917  ActionVectorEntry.Command = Second;
11918  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11919  }
11920  else if(FormatType == FSHNewService)
11921  {
11922  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
11923  {
11924  ;
11925  }
11926  ActionVectorEntry.Command = Second;
11927  ActionVectorEntry.OtherHeadCode = Third;
11928  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11929  }
11930  else if(FormatType == ExitRailway)
11931  {
11932  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
11933  {
11934  ;
11935  }
11936  ActionVectorEntry.Command = Second;
11937  ActionVectorEntry.ExitList = ExitList;
11938  }
11939  else if(FormatType == FinRemHere)
11940  {
11941  ActionVectorEntry.Command = Second;
11942  }
11943  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11944  }
11945  }
11946  else // repeat
11947  {
11948  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
11949  {
11950  Utilities->CallLogPop(786);
11951  // error messages given in SplitRepeat
11952  return(false);
11953  }
11954  if(FinalCall)
11955  {
11956  TActionVectorEntry ActionVectorEntry;
11957  ActionVectorEntry.FormatType = Repeat;
11958  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
11959  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
11960  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
11961  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11962  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11963  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
11964  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11965  }
11966  }
11967  }
11968  }
11969  if(FinalCall)
11970  {
11971  TrainDataVector.push_back(TempTrainDataEntry);
11972  }
11973  }
11974  Utilities->CallLogPop(80);
11975  return(true);
11976 }
11977 
11978 // ---------------------------------------------------------------------------
11979 
11980 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
11981 {
11982  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
11983  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
11984  {
11985  Utilities->CallLogPop(1890);
11986  return(false);
11987  }
11988  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
11989  {
11990  Utilities->CallLogPop(1891);
11991  return(false);
11992  }
11993  Utilities->CallLogPop(1892);
11994  return(true);
11995 }
11996 
11997 // ---------------------------------------------------------------------------
11998 
11999 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
12000 // 1st 5 chars must be HH:MM, anything else will be ignored
12001 {
12002  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
12003  if(TimeStr.Length() < 5)
12004  {
12005  Utilities->CallLogPop(926);
12006  return(false);
12007  }
12008  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
12009  {
12010  Utilities->CallLogPop(927);
12011  return(false);
12012  }
12013  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
12014  {
12015  Utilities->CallLogPop(928);
12016  return(false);
12017  }
12018  if(TimeStr[3] != ':')
12019  {
12020  Utilities->CallLogPop(929);
12021  return(false);
12022  }
12023  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
12024  {
12025  Utilities->CallLogPop(930);
12026  return(false);
12027  }
12028  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
12029  {
12030  Utilities->CallLogPop(931);
12031  return(false);
12032  }
12033  while(TimeStr.Length() > 5)
12034  {
12035  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
12036  }
12037  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
12038  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
12039 
12040  if((WholeHours + FracHour) >= 95.98334)
12041  {
12042  Utilities->CallLogPop(1817);
12043  return(false); // > 95h 59m
12044  }
12045  Time = TDateTime((WholeHours + FracHour) / 24);
12046  Utilities->CallLogPop(932);
12047  return(true);
12048 }
12049 
12050 // ---------------------------------------------------------------------------
12051 
12052 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
12053  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
12054  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
12055 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
12056  Return false for failure.
12057  See description above under ProcessOneTimetableLinefor details of train action entries
12058  NB all types set except LocationType for Snt as may be located or not
12059 */{
12060  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
12061  Warning = false;
12062  TDateTime TempTime;
12063 
12064  if(OneEntry.Length() > 0)
12065  {
12066  if(OneEntry[1] == 'W') // warning
12067  {
12068  Warning = true;
12069  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
12070  // strip it off
12071  }
12072  }
12073  if(OneEntry == "Frh")
12074  {
12075  FormatType = FinRemHere;
12076  SequenceType = FinishSequence;
12077  LocationType = AtLocation;
12078  ShuttleLinkType = NotAShuttleLink;
12079  Second = "Frh";
12080  Utilities->CallLogPop(1016);
12081  return(true);
12082  }
12083  if(OneEntry.Length() < 7)
12084  {
12085  Utilities->CallLogPop(907);
12086  return(false); // 'HH:MM;' + at least a one-letter location name
12087  }
12088  int Pos = OneEntry.Pos(';'); // first segment delimiter
12089 
12090  if(Pos != 6)
12091  {
12092  Utilities->CallLogPop(908);
12093  return(false);
12094  // no delimiter or delimiter not in position 6, has to be a time so fail
12095  }
12096  First = OneEntry.SubString(1, 5); // has to be a time
12097  if(!CheckTimeValidity(16, First, TempTime))
12098  {
12099  Utilities->CallLogPop(909);
12100  return(false);
12101  }
12102  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
12103 
12104  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
12105  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
12106  {
12107  if(Remainder.Length() < 7)
12108  {
12109  Utilities->CallLogPop(910);
12110  return(false); // 'HH:MM;' + at least a one-letter location name
12111  }
12112  Pos = Remainder.Pos(';'); // second segment delimiter
12113  if(Pos == 0)
12114  {
12115  Utilities->CallLogPop(911);
12116  return(false);
12117  // no delimiter, has to be one between departure time & location
12118  }
12119  Second = Remainder.SubString(1, 5); // has to be a time
12120  if(!CheckTimeValidity(15, Second, TempTime))
12121  {
12122  Utilities->CallLogPop(912);
12123  return(false);
12124  }
12125  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12126  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
12127  {
12128  Utilities->CallLogPop(913);
12129  return(false);
12130  }
12131  FormatType = TimeTimeLoc;
12132  SequenceType = IntermediateSequence;
12133  LocationType = AtLocation;
12134  ShuttleLinkType = NotAShuttleLink;
12135  Utilities->CallLogPop(914);
12136  return(true);
12137  }
12138  Pos = Remainder.Pos(';'); // second segment delimiter
12139  if(Pos == 0) // no third segment so second must be a location, or cdt
12140  {
12141  Second = Remainder;
12142  if(Second == "cdt")
12143  {
12144  FormatType = TimeCmd;
12145  ShuttleLinkType = NotAShuttleLink;
12146  LocationType = AtLocation;
12147  SequenceType = IntermediateSequence;
12148  Utilities->CallLogPop(915);
12149  return(true);
12150  }
12151  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
12152  {
12153  Utilities->CallLogPop(916);
12154  return(false);
12155  }
12156  else
12157  {
12158  FormatType = TimeLoc;
12159  LocationType = AtLocation;
12160  SequenceType = IntermediateSequence;
12161  ShuttleLinkType = NotAShuttleLink;
12162  Utilities->CallLogPop(917);
12163  return(true);
12164  }
12165  }
12166  // here if second segment is a command, with a third & maybe fourth segments
12167  if((Pos != 4) && (Pos != 7) && (Pos != 8))
12168  {
12169  Utilities->CallLogPop(918);
12170  return(false);
12171  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
12172  }
12173  Second = Remainder.SubString(1, Pos - 1); // command
12174 
12175  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12176  // details
12177  Pos = Remainder.Pos(';'); // third segment delimiter
12178  if(Pos == 0)
12179  {
12180  Third = Remainder;
12181  }
12182  else
12183  {
12184  Third = Remainder.SubString(1, Pos - 1);
12185  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12186  }
12187 
12188  if((Second == "Snt") || (Second == "Snt-sh"))
12189  // third has to be 2 element idents with a space between
12190  {
12191  int SpacePos = Third.Pos(' ');
12192  if(SpacePos == 0)
12193  {
12194  Utilities->CallLogPop(919);
12195  return(false); // no space
12196  }
12197  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
12198  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
12199  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
12200  if(CheckLocationsExistInRailway)
12201  {
12202  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
12203  {
12204  Utilities->CallLogPop(920);
12205  return(false);
12206  }
12207  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
12208  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
12209  }
12210  if(Second == "Snt")
12211  {
12212  FormatType = StartNew;
12213  SequenceType = StartSequence;
12214  LocationType = NoLocation;
12215  // can't be set until know whether located or not - done in SecondPassActions
12216  ShuttleLinkType = NotAShuttleLink;
12217  }
12218  else // Snt-sh
12219  {
12220  FormatType = SNTShuttle;
12221  LocationType = AtLocation;
12222  SequenceType = StartSequence;
12223  ShuttleLinkType = ShuttleLink;
12224  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
12225  {
12226  Utilities->CallLogPop(1038);
12227  return(false);
12228  }
12229  }
12230  Utilities->CallLogPop(921);
12231  return(true);
12232  }
12233  if(Second == "Sns-sh") // third & fourth have to be headcodes
12234  {
12235  FormatType = SNSShuttle;
12236  LocationType = AtLocation;
12237  SequenceType = StartSequence;
12238  ShuttleLinkType = ShuttleLink;
12239  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
12240  {
12241  Utilities->CallLogPop(1039);
12242  return(false);
12243  }
12244  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
12245  {
12246  Utilities->CallLogPop(1040);
12247  return(false);
12248  }
12249  Utilities->CallLogPop(1041);
12250  return(true);
12251  }
12252  if(Second == "F-nshs")
12253  {
12254  FormatType = FNSNonRepeatToShuttle;
12255  LocationType = AtLocation;
12256  SequenceType = FinishSequence;
12257  ShuttleLinkType = ShuttleLink;
12258  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
12259  {
12260  Utilities->CallLogPop(1047);
12261  return(false);
12262  }
12263  Utilities->CallLogPop(1048);
12264  return(true);
12265  }
12266  if(Second == "Sns-fsh")
12267  {
12268  FormatType = SNSNonRepeatFromShuttle;
12269  LocationType = AtLocation;
12270  SequenceType = StartSequence;
12271  ShuttleLinkType = ShuttleLink;
12272  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
12273  {
12274  Utilities->CallLogPop(1098);
12275  return(false);
12276  }
12277  Utilities->CallLogPop(1099);
12278  return(true);
12279  }
12280  if(Second == "Fns-sh") // third & fourth have to be headcodes
12281  {
12282  FormatType = FSHNewService;
12283  LocationType = AtLocation;
12284  SequenceType = FinishSequence;
12285  ShuttleLinkType = ShuttleLink;
12286  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
12287  {
12288  Utilities->CallLogPop(1050);
12289  return(false);
12290  }
12291  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
12292  {
12293  Utilities->CallLogPop(1051);
12294  return(false);
12295  }
12296  Utilities->CallLogPop(1052);
12297  return(true);
12298  }
12299  // new segment for 'pas'
12300  if(Second == "pas") // third has to be a location
12301  {
12302  FormatType = PassTime;
12303  LocationType = EnRoute;
12304  SequenceType = IntermediateSequence;
12305  ShuttleLinkType = NotAShuttleLink;
12306  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
12307  {
12308  Utilities->CallLogPop(1515);
12309  return(false);
12310  }
12311  Utilities->CallLogPop(1516);
12312  return(true);
12313  }
12314  // new segment for revised 'Fer'
12315  if(Second == "Fer")
12316  // third has to be a set of IDs separated by spaces, and at least 1
12317  {
12318  FormatType = ExitRailway;
12319  LocationType = EnRoute;
12320  SequenceType = FinishSequence;
12321  ShuttleLinkType = NotAShuttleLink;
12322  if(CheckLocationsExistInRailway)
12323  {
12324  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
12325  {
12326  Utilities->CallLogPop(1519);
12327  return(false);
12328  }
12329  }
12330  Utilities->CallLogPop(1520);
12331  return(true);
12332  }
12333  if(Second == "dsc") //new at v2.15.0 - change description
12334  {
12335  if(Third.Length() > 60)
12336  {
12337  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters in '" + Third + "'");
12338  Utilities->CallLogPop(2582);
12339  return(false);
12340  }
12341  for(int x = 1; x < Third.Length() + 1; x++)
12342  {
12343  if((Third[x] < ' ') || (Third[x] > '~'))
12344  {
12345  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + Third + "'");
12346  Utilities->CallLogPop(2583);
12347  return(false);
12348  }
12349  }
12350  FormatType = TimeCmdDescription;
12351  LocationType = AtLocation;
12352  SequenceType = IntermediateSequence;
12353  ShuttleLinkType = NotAShuttleLink;
12354  Utilities->CallLogPop(2604);
12355  return(true);
12356  }
12357 
12358 // if((Second == "fsp") || (Second == "fsp")) then there can optionlly be a fourth: xx-yy where xx = percentage mass & yy = percentage power in the split train
12359 
12360  // all remainder must be TimeCmdHeadCode types to be valid
12361  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
12362  (Second != "Frh-sh"))
12363  {
12364  Utilities->CallLogPop(922);
12365  return(false); // all TimeCmdHeadCode types
12366  }
12367  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
12368  {
12369  Utilities->CallLogPop(923);
12370  return(false);
12371  }
12372  FormatType = TimeCmdHeadCode;
12373  LocationType = AtLocation;
12374  if(Second == "Frh-sh")
12375  {
12376  ShuttleLinkType = ShuttleLink;
12377  }
12378  else
12379  {
12380  ShuttleLinkType = NotAShuttleLink;
12381  }
12382  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
12383  {
12384  SequenceType = FinishSequence;
12385  }
12386  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
12387  {
12388  SequenceType = IntermediateSequence;
12389  }
12390  if((Second == "Sfs") || (Second == "Sns"))
12391  {
12392  SequenceType = StartSequence;
12393  }
12394  //new at v2.15.0 to allow splits to have different characteristics - Fourth specifies: AA-BB where AA & BB can be 1 or 2 digits, AA is percentage mass
12395  if((Fourth != "") && ((Second == "fsp") || (Second == "rsp"))) //& BB is percentage power allocated to the split off train
12396  {
12397  if(!CheckFourthValidityForSplit(Fourth, GiveMessages))
12398  {
12399  Utilities->CallLogPop(2584);
12400  return(false);
12401  }
12402  }
12403  Utilities->CallLogPop(924);
12404  return(true);
12405 }
12406 
12407 // ---------------------------------------------------------------------------
12408 
12409 bool TTrainController::CheckFourthValidityForSplit(AnsiString SplitDistributionString, bool GiveMessages) //new at v2.15.0
12410 {
12411  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CheckFourthValidityForSplit," + SplitDistributionString);
12412  bool ErrorFlag = false;
12413  int x, y;
12414  if((SplitDistributionString.Length() > 6 ) || (SplitDistributionString.Length() < 3))
12415  {
12416  ErrorFlag = true;
12417  }
12418  int pos = SplitDistributionString.Pos('-');
12419  if(pos == 0)
12420  {
12421  ErrorFlag = true;
12422  }
12423  else
12424  {
12425  AnsiString MassStr = SplitDistributionString.SubString(1, pos - 1);
12426  AnsiString PowerStr = SplitDistributionString.SubString(pos + 1, SplitDistributionString.Length() - pos);
12427  try //allows for one or two digit percentages
12428  {
12429  int x = MassStr.ToInt();
12430  int y = PowerStr.ToInt();
12431  if((x > 99) || (x < 1) || (y > 100) || (y < 0))
12432  {
12433  ErrorFlag = true;
12434  }
12435  }
12436  catch(const Exception &e) //non-error catch
12437  {
12438  ErrorFlag = true;
12439  }
12440  }
12441  if(ErrorFlag)
12442  {
12443  TimetableMessage(GiveMessages, "Error in split distribution " + SplitDistributionString + ", should be 'AA-BB' where AA is the percentage mass (min 1, max 99) and BB the percentage " +
12444  "power for the new split-off train");
12445  Utilities->CallLogPop(2585);
12446  return(false);
12447  }
12448  Utilities->CallLogPop(2601);
12449  return(true);
12450 }
12451 
12452 // ---------------------------------------------------------------------------
12453 
12454 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
12455 {
12456  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
12457  // and contains no special characters
12458  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
12459  if(LocStr == "")
12460  {
12461  Utilities->CallLogPop(1353);
12462  return(false); // has to have at least one character
12463  }
12464  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
12465  {
12466  Utilities->CallLogPop(1354);
12467  return(false); // can't begin with a number
12468  }
12469  for(int x = 1; x < LocStr.Length() + 1; x++)
12470  {
12471  if(LocStr[x] < ' ')
12472  {
12473  Utilities->CallLogPop(1355);
12474  return(false); // contains a special character
12475  }
12476  if(LocStr[x] > 'z')
12477  {
12478  Utilities->CallLogPop(1356);
12479  return(false); // contains a character outside the standard ASCII set
12480  }
12481  }
12482  // check exists in railway location list if CheckLocationsExistInRailway is true
12483  if(CheckLocationsExistInRailway)
12484  {
12485  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
12486  {
12487  TimetableMessage(GiveMessages, "Location name '" + LocStr +
12488  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
12489  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
12490  "that includes a continuation will not be valid.");
12491  Utilities->CallLogPop(1357);
12492  return(false);
12493  }
12494  }
12495  Utilities->CallLogPop(1358);
12496  return(true);
12497 }
12498 
12499 // ---------------------------------------------------------------------------
12500 
12501 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
12502 {
12503  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
12504  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
12505  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
12506  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
12507  HeadCode);
12508  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
12509  {
12510  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
12511  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Sns, Snt-sh, Sns-sh, Fns, Fns-sh or Frh-sh");
12512  Utilities->CallLogPop(1359);
12513  return(false);
12514  }
12515  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
12516  for(int x = 1; x < (HeadCode.Length() + 1); x++)
12517  {
12518  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
12519  {
12520  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
12521  Utilities->CallLogPop(1895);
12522  return(false);
12523  }
12524  }
12525  // secondly ensure the true Headcode only has letters or digits
12526  for(int x = 3; x >= 0; x--)
12527  {
12528  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
12529  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
12530  {
12531  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
12532  Utilities->CallLogPop(1790);
12533  return(false);
12534  }
12535  }
12536  Utilities->CallLogPop(1364);
12537  return(true);
12538 }
12539 
12540 // ---------------------------------------------------------------------------
12541 
12542 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
12543 // set of track element IDs, separated by spaces, and at least 1 present
12544 {
12545  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndPopulateListOfIDs," + IDSet); //had wrong title, changed at v2.13.0
12546  ExitList.clear();
12547  AnsiString CurrentID = "";
12548 
12549  if(IDSet.Length() == 0)
12550  {
12551  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
12552  Utilities->CallLogPop(1521);
12553  return(false);
12554  }
12555  for(int x = 1; x <= IDSet.Length(); x++)
12556  {
12557  char C = IDSet[x];
12558  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
12559  {
12560  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
12561  Utilities->CallLogPop(1522);
12562  return(false);
12563  }
12564 /* don't use, error checks in GetTrackVectorPositionFromString instead
12565  if(C == '-') //this section added at v2.13.0 because of Amon Sadler's error file submitted 24/03/22
12566  {
12567  if((x==1) || (x == IDSet.Length()))
12568  {
12569  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
12570  Utilities->CallLogPop(2479);
12571  return(false);
12572  }
12573  if((IDSet[x-1] < '0') || (IDSet[x-1] > '9') || (IDSet[x+1] < '0') || (IDSet[x+1] > '9'))
12574  {
12575  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
12576  Utilities->CallLogPop(2480);
12577  return(false);
12578  }
12579  }
12580 */
12581  }
12582  int Pos = IDSet.Pos(' '); // look for the first space
12583 
12584  while(true)
12585  {
12586  if(Pos == 0)
12587  {
12588  CurrentID = IDSet;
12589  IDSet = "";
12590  }
12591  else
12592  {
12593  CurrentID = IDSet.SubString(1, Pos - 1);
12594  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
12595  }
12596  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
12597  if(VecPos == -1)
12598  {
12599  Utilities->CallLogPop(1523);
12600  return(false); // messages given in GetTrackVectorPositionFromString
12601  }
12602  else
12603  {
12604  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
12605  {
12606  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
12607  Utilities->CallLogPop(1524);
12608  return(false);
12609  }
12610  else
12611  {
12612  // first check for duplicates
12613  if(!ExitList.empty())
12614  {
12615  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
12616  {
12617  if(*ELIT == VecPos)
12618  {
12619  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
12620  Utilities->CallLogPop(1532);
12621  return(false);
12622  }
12623  }
12624  }
12625  // of OK add it to the list
12626  ExitList.push_back(VecPos);
12627  }
12628  }
12629  if(IDSet == "")
12630  {
12631  Utilities->CallLogPop(1525);
12632  return(true);
12633  }
12634  else
12635  {
12636  Pos = IDSet.Pos(' '); // look for the next space
12637  }
12638  } // while(true)
12639 }
12640 
12641 // ---------------------------------------------------------------------------
12642 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
12643  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
12644 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
12645 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
12646 // of each item
12647 {
12648  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
12649  int Pos = 0;
12650  AnsiString Remainder = "";
12651  int SemiColonCount = 0;
12652 
12653  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
12654  {
12655  if(TrainInfoStr[x] == ';')
12656  {
12657  SemiColonCount++;
12658  }
12659  }
12660  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
12661  {
12662  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
12663  "'. Should be headcode + optional description for a continuing service;" +
12664  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
12665  Utilities->CallLogPop(880);
12666  return(false);
12667  }
12668  if(SemiColonCount == 0)
12669  {
12670  HeadCode = TrainInfoStr;
12671  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
12672  {
12673  Utilities->CallLogPop(881);
12674  return(false);
12675  }
12676  Utilities->CallLogPop(882);
12677  return(true);
12678  }
12679  if(SemiColonCount == 1) // headcode & description only
12680  {
12681  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
12682  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
12683  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
12684  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
12685  {
12686  Utilities->CallLogPop(883);
12687  return(false);
12688  }
12689  if(Description == "")
12690  {
12691  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
12692  Utilities->CallLogPop(884);
12693  return(false);
12694  }
12695  if(Description.Length() > 60)
12696  {
12697  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
12698  Utilities->CallLogPop(1157);
12699  return(false);
12700  }
12701  for(int x = 1; x < Description.Length() + 1; x++)
12702  {
12703  if((Description[x] < ' ') || (Description[x] > '~'))
12704  {
12705  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
12706  Utilities->CallLogPop(885);
12707  return(false);
12708  }
12709  }
12710  Utilities->CallLogPop(886);
12711  return(true);
12712  }
12713  // if here must have 6 or 7 semicolons
12714  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
12715  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
12716  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
12717  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
12718  {
12719  Utilities->CallLogPop(887);
12720  return(false);
12721  }
12722  Pos = Remainder.Pos(';'); // 2nd delimiter
12723  Description = Remainder.SubString(1, Pos - 1);
12724  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12725  if(Description == "")
12726  {
12727  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
12728  Utilities->CallLogPop(888);
12729  return(false);
12730  }
12731  if(Description.Length() > 60)
12732  {
12733  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
12734  Utilities->CallLogPop(1158);
12735  return(false);
12736  }
12737  for(int x = 1; x < Description.Length() + 1; x++)
12738  {
12739  if((Description[x] < ' ') || (Description[x] > 126))
12740  {
12741  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
12742  Utilities->CallLogPop(889);
12743  return(false);
12744  }
12745  }
12746  Pos = Remainder.Pos(';'); // 3rd delimiter
12747  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
12748 
12749  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12750  if(StartSpeedStr == "")
12751  {
12752  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
12753  Utilities->CallLogPop(890);
12754  return(false);
12755  }
12756  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
12757  {
12758  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
12759  {
12760  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
12761  Utilities->CallLogPop(891);
12762  return(false);
12763  }
12764  }
12765  StartSpeed = StartSpeedStr.ToInt();
12766  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
12767  {
12768  StartSpeed = TTrain::MaximumSpeedLimit;
12769  if(!SSHigh) // added at v2.4.0
12770  {
12771  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12772  SSHigh = true;
12773  }
12774  }
12775  Pos = Remainder.Pos(';'); // 4th delimiter
12776  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
12777 
12778  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12779  if(MaxRunningSpeedStr == "")
12780  {
12781  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
12782  Utilities->CallLogPop(892);
12783  return(false);
12784  }
12785  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
12786  {
12787  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
12788  {
12789  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
12790  Utilities->CallLogPop(893);
12791  return(false);
12792  }
12793  }
12794  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
12795  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
12796  {
12797  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
12798  if(!MRSHigh) // added at v2.4.0
12799  {
12800  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12801  MRSHigh = true;
12802  }
12803  }
12804  if(MaxRunningSpeed < 10)
12805  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
12806  {
12807  MaxRunningSpeed = 10;
12808  if(!MRSLow) // added at v2.4.0
12809  {
12810  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
12811  MRSLow = true;
12812  }
12813  }
12814  Pos = Remainder.Pos(';'); // 5th delimiter
12815  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
12816 
12817  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12818  if(MassStr == "")
12819  {
12820  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
12821  Utilities->CallLogPop(895);
12822  return(false);
12823  }
12824  for(int x = 1; x < MassStr.Length() + 1; x++)
12825  {
12826  if((MassStr[x] < '0') || (MassStr[x] > '9'))
12827  {
12828  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
12829  Utilities->CallLogPop(896);
12830  return(false);
12831  }
12832  }
12833  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
12834  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
12835  {
12836  Mass = TTrain::MaximumMassLimit;
12837  if(!MassHigh) // added at v2.4.0
12838  {
12839  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
12840  MassHigh = true;
12841  }
12842  }
12843  if(Mass == 0)
12844  {
12845  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
12846  Utilities->CallLogPop(897);
12847  return(false);
12848  }
12849  Pos = Remainder.Pos(';'); // 6th delimiter
12850  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
12851 
12852  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12853  if(MaxBrakeForceStr == "")
12854  {
12855  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
12856  Utilities->CallLogPop(898);
12857  return(false);
12858  }
12859  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
12860  {
12861  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
12862  {
12863  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
12864  Utilities->CallLogPop(899);
12865  return(false);
12866  }
12867  }
12868  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
12869 
12870  // convert to kg force
12871  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
12872  {
12873  MaxBrakeForce = Mass;
12874  if(!BFHigh) // added at v2.4.0
12875  {
12876  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
12877  BFHigh = true;
12878  }
12879  }
12880  if((MaxBrakeForce / Mass) < 0.01)
12881  {
12882  MaxBrakeForce = Mass * 0.01;
12883  if(!BFLow) // added at v2.4.0
12884  {
12885  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
12886  BFLow = true;
12887  }
12888  }
12889  // convert to m/s/s
12890  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
12891  // now may have just a power entry or power and signaller max. speed
12892  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
12893 
12894  if(SemiColonCount == 6)
12895  {
12896  GrossPowerStr = Remainder;
12897  SignallerSpeedStr = "30"; // default value
12898  }
12899  else // must be 7
12900  {
12901  Pos = Remainder.Pos(';'); // 7th delimiter
12902  GrossPowerStr = Remainder.SubString(1, Pos - 1);
12903  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12904  }
12905  // deal with GrossPower
12906  if(GrossPowerStr == "")
12907  {
12908  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
12909  Utilities->CallLogPop(901);
12910  return(false);
12911  }
12912  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
12913  {
12914  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
12915  {
12916  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
12917  Utilities->CallLogPop(902);
12918  return(false);
12919  }
12920  }
12921 
12922  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
12923 
12924  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
12925  {
12926  GrossPower = TTrain::MaximumPowerLimit;
12927  if(!PwrHigh)
12928  {
12929  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
12930  PwrHigh = true;
12931  }
12932  }
12933  else if(GrossPower == 0) // changed at v2.4.0
12934  {
12935  GrossPower = 0.1;
12936  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
12937  }
12938  else if((GrossPower > 0) && (GrossPower < 10000))
12939  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
12940  {
12941  GrossPower = 10000;
12942  }
12943  PowerAtRail = GrossPower * 0.8;
12944  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
12945 
12946  // deal with SignallerSpeed
12947  if(SignallerSpeedStr == "")
12948  {
12949  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
12950  Utilities->CallLogPop(1771);
12951  return(false);
12952  }
12953  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
12954  {
12955  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
12956  {
12957  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
12958  Utilities->CallLogPop(1769);
12959  return(false);
12960  }
12961  }
12962  SignallerSpeed = SignallerSpeedStr.ToInt();
12963  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
12964  {
12965  SignallerSpeed = TTrain::MaximumSpeedLimit;
12966  if(!SigSHigh)
12967  {
12968  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12969  SigSHigh = true;
12970  }
12971  }
12972  if(SignallerSpeed < 10)
12973  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
12974  {
12975  SignallerSpeed = 10;
12976  if(!SigSLow)
12977  {
12978  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
12979  SigSLow = true;
12980  }
12981  // Utilities->CallLogPop(1770);
12982  // return false;
12983  }
12984  Utilities->CallLogPop(904);
12985  return(true);
12986 }
12987 
12988 // ---------------------------------------------------------------------------
12989 
12990 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
12991  bool GiveMessages)
12992 {
12993  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
12994  // function checks validity of each item and returns false for error
12995  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
12996  if(OneEntry.Length() < 7)
12997  {
12998  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12999  Utilities->CallLogPop(865);
13000  return(false);
13001  }
13002  int SemiColonCount = 0;
13003 
13004  for(int x = 1; x < OneEntry.Length() + 1; x++)
13005  {
13006  if(OneEntry[x] == ';')
13007  {
13008  SemiColonCount++;
13009  }
13010  }
13011  if(SemiColonCount != 3)
13012  {
13013  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
13014  Utilities->CallLogPop(866);
13015  return(false);
13016  }
13017  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
13018  {
13019  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
13020  Utilities->CallLogPop(867);
13021  return(false);
13022  }
13023  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
13024  // strip off R;
13025 
13026  int Pos = 0;
13027 
13028  Pos = Remainder.Pos(';');
13029  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
13030 
13031  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
13032  if(MinutesStr == "")
13033  {
13034  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
13035  Utilities->CallLogPop(868);
13036  return(false);
13037  }
13038  if(MinutesStr.Length() > 3)
13039  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
13040  {
13041  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
13042  Utilities->CallLogPop(2119);
13043  return(false);
13044  }
13045  for(int x = 1; x < MinutesStr.Length() + 1; x++)
13046  {
13047  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
13048  {
13049  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
13050  Utilities->CallLogPop(869);
13051  return(false);
13052  }
13053  }
13054  RearStartOrRepeatMins = MinutesStr.ToInt();
13055  if(RearStartOrRepeatMins == 0)
13056  {
13057  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
13058  Utilities->CallLogPop(870);
13059  return(false);
13060  }
13061  Pos = Remainder.Pos(';');
13062  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
13063 
13064  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
13065  if(DigitsStr == "")
13066  {
13067  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
13068  Utilities->CallLogPop(871);
13069  return(false);
13070  }
13071  for(int x = 1; x < DigitsStr.Length() + 1; x++)
13072  {
13073  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
13074  {
13075  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
13076  Utilities->CallLogPop(872);
13077  return(false);
13078  }
13079  }
13080  if(DigitsStr.Length() > 2)
13081  {
13082  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
13083  Utilities->CallLogPop(873);
13084  return(false);
13085  }
13086  FrontStartOrRepeatDigits = DigitsStr.ToInt();
13087 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
13088  route rather than the service
13089  if(FrontStartOrRepeatDigits == 0)
13090  {
13091  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
13092  Utilities->CallLogPop(874);
13093  return false;
13094  }
13095 */
13096  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
13097  // new for v0.6b for unrestricted headcodes
13098  {
13099  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
13100  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
13101  Utilities->CallLogPop(1889);
13102  return(false);
13103  }
13104  AnsiString NumberStr = Remainder;
13105 
13106  if(NumberStr == "")
13107  {
13108  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
13109  Utilities->CallLogPop(875);
13110  return(false);
13111  }
13112  if(NumberStr.Length() > 4)
13113  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
13114  {
13115  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
13116  Utilities->CallLogPop(2118);
13117  return(false);
13118  }
13119  for(int x = 1; x < NumberStr.Length() + 1; x++)
13120  {
13121  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
13122  // catches negative numbers
13123  {
13124  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
13125  Utilities->CallLogPop(876);
13126  return(false);
13127  }
13128  }
13129  NumberOfRepeats = NumberStr.ToInt();
13130  if(NumberOfRepeats == 0)
13131  {
13132  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
13133  Utilities->CallLogPop(877);
13134  return(false);
13135  }
13136  Utilities->CallLogPop(878);
13137  return(true);
13138 }
13139 
13140 // ---------------------------------------------------------------------------
13141 
13142 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
13143 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
13144  vector rather than the timetable
13145  Note also that for unlocated Snt entries the LocationType hasn't yet been set
13146 
13147  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
13148 
13149  For info:-
13150  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
13151  {
13152  public:
13153  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled event entries, null
13155  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
13156  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
13157  int NumberOfRepeats; ///< the number of repeating services
13158  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
13160  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
13162  TNumList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
13163  TTimetableFormatType FormatType; ///< defines the timetable action type
13164  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
13165  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
13166  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
13167  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
13169  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
13171 
13172  // inline function
13173 
13175  TActionVectorEntry() {
13176  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
13177  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
13178  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
13179  Warning = false; SignallerControl = false;
13180  }
13181  };
13182 
13183  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
13184 
13185  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
13186  {
13187  public:
13188  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
13191  double MaxBrakeRate; ///< in metres/sec/sec
13192  double MaxRunningSpeed; ///< in km/h
13193  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
13194  int Mass; ///< in kg
13195  int NumberOfTrains; ///< number of repeats + 1
13196  int SignallerSpeed; ///< in km/h for use when under signaller control
13197  int StartSpeed; ///< in km/h
13198  TActionVector ActionVector; ///< all the actions for the train
13199  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
13200 
13201  //inline function
13202 
13204  TTrainDataEntry()
13205  {
13206  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
13207  }
13208  };
13209 
13210  Allowable successors:-
13211  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
13212  Snt located -> No starts, no finishes except Frh, Fjo (as of v2.0.0), Fns, and F-nshs, no repeat, pas, TimeTimeLoc or TimeLoc arr; any other cmd or TimeLoc (dep) OK
13213  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
13214  Sfs -> No starts, finishes except Frh & Fjo (as of v2.15.0), repeats, pas, TimeTimeLoc, TimeLoc arr, rsp, fsp; any other cmd or TimeLoc (dep) OK [
13215  must have departure & arrival before another split]
13216  Sns -> No starts, finishes except Frh & Fjo (as of v2.15.0), repeats, pas, TimeTimeLoc or TimeLoc arr; any other cmd or TimeLoc (dep) OK
13217  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
13218  set location, else fails)
13219  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
13220  set location, else fails)
13221  Fns -> R only [must be preceded by a TimeLoc arrival at the finish location, not necessarily immediately]
13222  F-nshs -> Nothing (no repeats permitted)
13223  Fjo -> R only
13224  Frh -> R only
13225  Fer -> R only
13226  Frh-sh -> R only
13227  Fns-sh -> R only
13228  jbo -> No starts, repeats, pas, Fer or TimeTimeLoc; TimeLoc (dep), others OK [must be preceded by an event whose location is set]
13229  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
13230  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
13231  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
13232  dsc -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
13233  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
13234  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
13235  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
13236  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
13237  Repeat -> Nothing
13238 
13239  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
13240  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
13241  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
13242  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
13243  Check all times increase or stay same through ActionVector
13244  Cycle through all entries in vector setting arr & dep times based on above list
13245  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
13246  Check locations match the arr & dep TimeLoc entries
13247  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
13248  Make above valid succession checks
13249  Check all splits have matching Sfs headcodes (both ways), add locations to Sfs's & check times same
13250  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to Sns's & check times same
13251  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
13252  Check each Fns has matching Sns headcodes (both ways), add locations to Fns's & check times same
13253  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
13254  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
13255  Set train info for Sfs & Sns entries
13256  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
13257  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
13258  element at each end, or length of 3 & 1 extra element at either end
13259  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
13260  Check all Cmds have EventTime set & Arr & Dep times = -1
13261  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
13262 
13263  Give messages in function if errors detected and clear the vector. Return false for failure.
13264 */
13265 
13266 /* Earlier checks:-
13267  Checks carried out with error messages in this function:-
13268  At least one comma in the line (it's based on a csv file);
13269  No entries following train information;
13270  At least one comma in remainder after train information (i.e at least a start and a finish entry);
13271  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
13272  First entry not a start entry;
13273  Train information incomplete before a start entry;
13274  Entry follows a finish entry but doesn't begin with 'R';
13275  SplitEntry returns false in a finish entry - message repeats the entry for information;
13276  Last action entry isn't a finish entry.
13277 
13278  Function returns false with no message if:-
13279  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
13280  time is found at all then an error message is given in the calling function);
13281  SplitTrainInfo returns false (message given in called function);
13282  SplitRepeat returns false (message given in called function).
13283 
13284 Double crosslink (shuttle) table: [OtherHeadCode, NonRepeatingShuttleLinkHeadCode, LinkedTrainEntryPtr, NonRepeatingShuttleLinkEntryPtr] <-- these for easier searching for this table
13285 
13286 Command Format OtherHead NonRepeating- LinkedTrain- NonRepeating- Decsription
13287  Code ShuttleLink- EntryPtr ShuttleLink
13288  HeadCode EntryPtr
13289 
13290 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
13291 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
13292 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
13293 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
13294 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
13295 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
13296 
13297 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
13298 
13299 */
13300 {
13301  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
13302  if(TrainDataVector.empty())
13303  {
13304  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
13305  TrainDataVector.clear();
13306  Utilities->CallLogPop(1832);
13307  return(false);
13308  }
13309 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
13310  1) must have at least one actionvector entry
13311  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
13312  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
13313  4) first entry must be a start;
13314  4a) if first entry is Snt and second is a finish then it can't be Fns-sh or Frh-sh
13315  4b) if first entry is Sns or Sfs and second is a finish then it must be either Frh or Fjo
13316  4c) if first entry is Snt-sh second can't be a finish
13317  5) a start must be the first entry;
13318  6) a repeat entry must be the last;
13319  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
13320  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
13321  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
13322 */
13323 
13324  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
13325  TwoLocationFlag = false; //added at v2.9.1
13326  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
13327  {
13328  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13329  if(TrainDataVector.at(x).ActionVector.empty())
13330  {
13331  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
13332  TrainDataVector.clear();
13333  Utilities->CallLogPop(1833);
13334  return(false);
13335  }
13336  }
13337  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
13338  {
13339  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13340  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13341  if(!(AVEntry0.SignallerControl))
13342  {
13343  if(TrainDataVector.at(x).ActionVector.size() == 1)
13344  {
13345  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
13346  TrainDataVector.clear();
13347  Utilities->CallLogPop(1822);
13348  return(false);
13349  }
13350  }
13351  }
13352  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
13353  {
13354  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13355  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13356  if(AVEntry0.SignallerControl)
13357  {
13358  if(TrainDataVector.at(x).ActionVector.size() > 2)
13359  {
13360  SecondPassMessage(GiveMessages,
13361  "Error in timetable - a signaller controlled service can have no more than one item (a repeat) after the start event, see: " +
13362  TDEntry.HeadCode);
13363  TrainDataVector.clear();
13364  Utilities->CallLogPop(1837);
13365  return(false);
13366  }
13367  if(TrainDataVector.at(x).ActionVector.size() > 1)
13368  {
13369  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13370  if(AVEntry1.FormatType != Repeat)
13371  {
13372  SecondPassMessage(GiveMessages,
13373  "Error in timetable - a signaller controlled service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
13374  TrainDataVector.clear();
13375  Utilities->CallLogPop(1838);
13376  return(false);
13377  }
13378  }
13379  }
13380  }
13381  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
13382  {
13383  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13384  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13385  if(AVEntry0.SequenceType != StartSequence)
13386  {
13387  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
13388  TrainDataVector.clear();
13389  Utilities->CallLogPop(1824);
13390  return(false);
13391  }
13392  if((AVEntry0.Command == "Snt") && !AVEntry0.SignallerControl) // (4a) sig control condition added so there is a second AVEntry
13393  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
13394  // and others for a located Snt, but those checks done later
13395  {
13396  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1); // must be a second entry if first not signallercontrol
13397  if((AVEntry1.SequenceType == FinishSequence) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
13398  {
13399  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
13400  TDEntry.HeadCode);
13401  TrainDataVector.clear();
13402  Utilities->CallLogPop(2046);
13403  return(false);
13404  }
13405  }
13406  if((AVEntry0.Command == "Sns") || (AVEntry0.Command == "Sfs")) // (4b)
13407  // 4b added at v2.15.0
13408  {
13409  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13410  if((AVEntry1.SequenceType == FinishSequence) && (AVEntry1.Command != "Frh") && (AVEntry1.Command != "Fjo"))
13411  {
13412  SecondPassMessage(GiveMessages, "Error in timetable - only 'Frh' or 'Fjo' finish events are permitted immediately after an 'Sns' or 'Sfs' entry for: " +
13413  TDEntry.HeadCode);
13414  TrainDataVector.clear();
13415  Utilities->CallLogPop(2580);
13416  return(false);
13417  }
13418  }
13419  if((AVEntry0.Command == "Snt-sh") || (AVEntry0.Command == "Sns-sh") || (AVEntry0.Command == "Sns-fsh")) // (4c)
13420  // 4c added at v2.15.0
13421  {
13422  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13423  if(AVEntry1.SequenceType == FinishSequence)
13424  {
13425  SecondPassMessage(GiveMessages, "Error in timetable - a finish event can't immediately follow an 'Snt-sh', 'Sns-sh' or 'Sns-fsh' event for: " + TDEntry.HeadCode);
13426  TrainDataVector.clear();
13427  Utilities->CallLogPop(2616);
13428  return(false);
13429  }
13430  }
13431  }
13432  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
13433  {
13434  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13435  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13436  {
13437  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13438  if((AVEntry.SequenceType == StartSequence) && (y != 0))
13439  {
13440  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
13441  TrainDataVector.clear();
13442  Utilities->CallLogPop(1825);
13443  return(false);
13444  }
13445  }
13446  }
13447  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
13448  {
13449  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13450  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13451  {
13452  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13453  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
13454  {
13455  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
13456  TrainDataVector.clear();
13457  Utilities->CallLogPop(1826);
13458  return(false);
13459  }
13460  }
13461  }
13462  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
13463  {
13464  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13465  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13466  {
13467  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13468  if((y == 0) && AVEntry.SignallerControl)
13469  {
13470  break;
13471  }
13472  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
13473  {
13474  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != FinishSequence))
13475  {
13476  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
13477  TrainDataVector.clear();
13478  Utilities->CallLogPop(1827);
13479  return(false);
13480  }
13481  if(AVEntry.FormatType == Repeat)
13482  {
13483  const TActionVectorEntry &LastButOneAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
13484  if(LastButOneAVEntry.SequenceType != FinishSequence)
13485  {
13486  SecondPassMessage(GiveMessages, "Error in timetable - the event immediately before the repeat must be a finish for: " + TDEntry.HeadCode);
13487  TrainDataVector.clear();
13488  Utilities->CallLogPop(1828);
13489  return(false);
13490  }
13491  }
13492  }
13493  }
13494  }
13495  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
13496  {
13497  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13498  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13499  {
13500  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13501  if(AVEntry.SequenceType == FinishSequence)
13502  {
13503  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
13504  {
13505  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
13506  TrainDataVector.clear();
13507  Utilities->CallLogPop(1829);
13508  return(false);
13509  }
13510  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
13511  {
13512  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
13513  {
13514  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " + TDEntry.HeadCode);
13515  TrainDataVector.clear();
13516  Utilities->CallLogPop(1830);
13517  return(false);
13518  }
13519  }
13520  }
13521  }
13522  }
13523 
13524  // end of new preliminary checks
13525 
13526  // check ActionVector present and check start event successor validity
13527  // For Snt & Snt-sh set location if stopped, don't set any times yet
13528  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13529  {
13530  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13531  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13532  // use reference so can change internals where necessary
13533  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
13534  {
13535  AnsiString LocationName = "";
13536  if(IsSNTEntryLocated(0, TDEntry, LocationName))
13537  // it is at a location
13538  {
13539  AVEntry0.LocationName = LocationName; //located Snt location name set
13540  AVEntry0.LocationType = AtLocation;
13541  // check successor validity for located Snt that isn't a SignallerControl entry
13542  if(!AVEntry0.SignallerControl)
13543  {
13544  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13545  // at least 2 entries present checked in integrity check so (1) valid
13546  if(!AtLocSuccessor(AVEntry1))
13547  {
13548  // Frh following Snt-sh will return false in location check, so no need to check here
13549  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
13550  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
13551  TrainDataVector.clear();
13552  Utilities->CallLogPop(523);
13553  return(false);
13554  }
13555  }
13556  }
13557  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
13558  {
13559  if(AVEntry0.Command == "Snt-sh")
13560  {
13561  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
13562  TrainDataVector.clear();
13563  Utilities->CallLogPop(1042);
13564  return(false);
13565  }
13566  AVEntry0.LocationType = EnRoute;
13567  if(!AVEntry0.SignallerControl)
13568  {
13569  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13570  // at least 2 entries checked in integrity check so (1) valid
13571  if(!MovingSuccessor(AVEntry1))
13572  {
13573  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
13574  TDEntry.HeadCode);
13575  TrainDataVector.clear();
13576  Utilities->CallLogPop(790);
13577  return(false);
13578  }
13579  }
13580  }
13581  }
13582  // check other start successors, all AtLoc
13583  else if(AVEntry0.SequenceType == StartSequence)
13584  {
13585  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13586  // at least 2 entries present checked in integrity check so (1) valid
13587  if(!AtLocSuccessor(AVEntry1))
13588  {
13589  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh', 'Snt-fsh' or 'Sns-fsh' followed by an illegal event for: " +
13590  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
13591  TrainDataVector.clear();
13592  Utilities->CallLogPop(793);
13593  return(false);
13594  }
13595  }
13596  }
13597 
13598 
13599  // set Sns-sh & Sns-fsh locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
13600  for(unsigned int x = 0; x < TrainDataVector.size(); x++) //at v2.15.0 set Sfs & Sns locations from corresponding fsp/rsp & Fns entries, shuttles ok as they are
13601  {
13602  bool FoundFlag = false;
13603  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13604  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13605  // use reference so can change internals
13606  if((AVEntry0.Command == "Sns-sh") || (AVEntry0.Command == "Sns-fsh"))
13607  {
13608  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
13609  {
13610  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
13611  if(AVEntry2.FormatType == TimeLoc)
13612  {
13613  FoundFlag = true;
13614  AVEntry0.LocationName = AVEntry2.LocationName; //Sns-sh & Sns-fsh location names set
13615  break;
13616  }
13617  }
13618  if(!FoundFlag)
13619  {
13620  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sns-sh' or 'Sns-fsh' event for: " + TDEntry.HeadCode);
13621  TrainDataVector.clear();
13622  Utilities->CallLogPop(851);
13623  return(false);
13624  }
13625  }
13626  }
13627 
13628 //carry out preliminary check on service ref linkages without setting any data - added at v2.15.0 as can be location errors if linked trains not present
13629 //first check for duplicates then linkages (also checked later but leave that in)
13630  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13631  {
13632  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13633  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13634  {
13635  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13636  if(AVEntry.OtherHeadCode != "")
13637  {
13638  if(!CheckForDuplicateCrossReferences(2, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
13639  {
13640  Utilities->CallLogPop(2610);
13641  return(false); // error message given in called function
13642  }
13643  }
13644  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13645  {
13646  if(!CheckForDuplicateCrossReferences(3, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
13647  {
13648  Utilities->CallLogPop(2611);
13649  return(false); // error message given in called function
13650  }
13651  }
13652  }
13653  }
13654 //cross reference check
13655  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13656  {
13657  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13658  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13659  {
13660  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13661  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13662  {
13663  if(AVEntry.OtherHeadCode != "")
13664  {
13665  if(!CheckCrossReferencesAndSetData(2, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, false, GiveMessages))
13666  // false = non-shuttle
13667  {
13668  Utilities->CallLogPop(2612);
13669  return(false); // error message given in called function
13670  }
13671  }
13672  }
13673  }
13674  }
13675 
13676 // now repeat the check just for the shuttles
13677  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13678  {
13679  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13680  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13681  {
13682  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13683  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
13684  {
13685  if(AVEntry.OtherHeadCode != "")
13686  {
13687  if(!CheckCrossReferencesAndSetData(3, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, false, GiveMessages))
13688  // true = shuttle
13689  {
13690  Utilities->CallLogPop(2613);
13691  return(false); // error message given in called function
13692  }
13693  }
13694  }
13695  }
13696  }
13697 
13698 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
13699  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13700  {
13701  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13702  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13703  {
13704  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13705  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13706  {
13707  if(!CheckNonRepeatingShuttleLinksAndSetData(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, false, GiveMessages))
13708  {
13709  Utilities->CallLogPop(2614);
13710  return(false); // error message given in called function
13711  }
13712  }
13713  }
13714  }
13715 
13716 
13717 //at v2.15.0 we want to set Sns, Sfs location names, but to set Sns & Sfs first need the linked Fns & fsp/rsp to have locations set as they aren't yet,
13718 //and before v2.15.0 they were set from the corresponding Sns & Sfs locations, which in turn were set from later TimeLoc departures. At v2.15.0 it is required to have
13719 //these commands followed by Frh & Fjo, so this is why we need the linked Fns & fsp/rsp to have locations set first. Now all Fns will have a TimeLoc before, so
13720 //that can provide its location, but fsp/rsp? Must they have a TimeLoc before? No, and can't rely on starting Sfs having the location set yet.
13721 //So, new restriction, insist on an rsp/fsp having a TimeLoc before it or a located Snt, and use one of those to set the location for the rsp/fsp and hence the linked Sfs.
13722 
13723 //NB can't allow an Sfs to be followed by another split or won't find a name, test with many existing tts then add an error to find it
13724 //Fns must be preceded by an arrival
13725 
13726 //set name for Fns from earlier location name or fail if can't find
13727  bool LocFoundFlag, FnsFoundFlag;
13728  TActionVectorEntry *AVEntryFns;
13729  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13730  {
13731  LocFoundFlag = false;
13732  FnsFoundFlag = false;
13733  for(int y = TrainDataVector.at(x).ActionVector.size() - 1; y >= 0; y--)
13734  {
13735  if(TrainDataVector.at(x).ActionVector.at(y).Command == "Fns")
13736  {
13737  AVEntryFns = &TrainDataVector.at(x).ActionVector.at(y);
13738  FnsFoundFlag = true;
13739  continue;
13740  }
13741  if(!FnsFoundFlag)
13742  {
13743  continue;
13744  }
13745  if(TrainDataVector.at(x).ActionVector.at(y).LocationName != "")
13746  {
13747  LocFoundFlag = true;
13748  AVEntryFns->LocationName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
13749 // double EVT = double(AVEntryFns->EventTime); //test
13750  break; //name found
13751  }
13752  if(TrainDataVector.at(x).ActionVector.at(y).LocationType == AtLocation) //not named yet
13753  {
13754  continue;
13755  }
13756  else
13757  {
13758  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' finish must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13759  TrainDataVector.clear();
13760  Utilities->CallLogPop(2596);
13761  return(false);
13762  }
13763  }
13764  if(FnsFoundFlag && !LocFoundFlag)
13765  {
13766  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' finish must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13767  TrainDataVector.clear();
13768  Utilities->CallLogPop(2597);
13769  return(false);
13770  }
13771  }
13772 
13773 //now set all names for Sns entries from the above, new at v2.15.0
13774  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13775  {
13776  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13777  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13778  // use reference so can change internals
13779  if(AVEntry0.Command == "Sns")
13780  {
13781  //new at v2.15.0. Only set location if have a forward and backward linkage at same time (these all set when SecondPassActions called). This isn't
13782  //rigorous as may have more than one, but if do then will be caught below in CheckCrossReferencesAndSetData
13783  //If fail to find location just ignore as will be caught later in CheckCrossReferencesAndSetData
13784  //note that at this stage the OtherHeadCode values are service refs, as haven't yet been changed back to headcodes until StripExcessFromHeadCode called
13785  //at end of this function
13786  //need to be the same: forward & reverse service refs, event times, commands correspond
13787 
13788  //successor checks first: /no starts, /finishes except Frh & Fjo (as of v2.15.0), /repeats, /pas, /TimeTimeLoc or /TimeLoc arr; any other cmd or TimeLoc (dep) OK
13789  if(TDEntry.ActionVector.size() < 2)
13790  {
13791  SecondPassMessage(GiveMessages, "Error in timetable - insufficient actions follwing an 'Sns' event for: " + TDEntry.HeadCode);
13792  TrainDataVector.clear();
13793  Utilities->CallLogPop(2598);
13794  return(false);
13795  }
13796  TActionVectorEntry AVEntry1 = TDEntry.ActionVector.at(1);
13797  if(!AtLocSuccessor(AVEntry1))
13798  {
13799  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sns' entry is followed by an illegal event for: " + TDEntry.HeadCode +
13800  ". The event isn't valid for a stationary train.");
13801  TrainDataVector.clear();
13802  Utilities->CallLogPop(2599);
13803  return(false);
13804  }
13805  if((AVEntry1.SequenceType == StartSequence) || ((AVEntry1.SequenceType == FinishSequence) && (AVEntry1.Command != "Frh") && (AVEntry1.Command != "Fjo")) ||
13806  (AVEntry1.FormatType == Repeat))
13807  {
13808  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sns' entry is followed by an illegal event for: " + TDEntry.HeadCode);
13809  TrainDataVector.clear();
13810  Utilities->CallLogPop(2600);
13811  return(false);
13812  }
13813 
13814  //now set the location and location type
13815  TDateTime SnsEventTime = AVEntry0.EventTime;
13816 // double EVT = double(SnsEventTime); //test
13817  AnsiString SnsServiceRef = TDEntry.ServiceReference;
13818  bool BreakFlag = false;
13819  for(unsigned int y = 0; y < TrainDataVector.size(); y++)
13820  {
13821  for(unsigned int z = 0; z < TrainDataVector.at(y).ActionVector.size(); z++)
13822  {
13823  if((TrainDataVector.at(y).ActionVector.at(z).Command == "Fns") && (SnsEventTime == TrainDataVector.at(y).ActionVector.at(z).EventTime) &&
13824  (TrainDataVector.at(y).HeadCode == AVEntry0.OtherHeadCode))
13825  { //forward linkage found
13826  if(TrainDataVector.at(y).ActionVector.at(z).OtherHeadCode == SnsServiceRef) //OtherHeadCode values are service refs, see above
13827  { //reverse linkage found
13828  AVEntry0.LocationName = TrainDataVector.at(y).ActionVector.at(z).LocationName;
13829  BreakFlag = true;
13830  break;
13831  }
13832  }
13833  }
13834  if(BreakFlag)
13835  {
13836  break;
13837  }
13838  }
13839  //test for any unnamed AtLoc entries at end of name setting
13840  }
13841  }
13842 
13843 //trap errors where rsp/fsp follows an Sfs without a TimeLoc arrival before (or unlikely to be able to set fsp/rsp/Sfs location)
13844  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13845  {
13846  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13847  if(AVEntry0.Command == "Sfs")
13848  {
13849  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
13850  {
13851  if(TrainDataVector.at(x).ActionVector.at(y).LocationName != "") //must be a timeloc as only they have loc set and are AtLoc (non-AtLoc trapped above)
13852  {
13853  break;
13854  }
13855  else if((TrainDataVector.at(x).ActionVector.at(y).Command == "fsp") || (TrainDataVector.at(x).ActionVector.at(y).Command == "rsp"))
13856  {
13857  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' action must be followed by a departure and arrival before another split, see " + TrainDataVector.at(x).ServiceReference);
13858  TrainDataVector.clear();
13859  Utilities->CallLogPop(2586);
13860  return(false);
13861  }
13862  }
13863  }
13864  }
13865 
13866 //now name fsp/rsp actions
13867  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13868  {
13869  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13870  {
13871  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13872  if(AVEntry.LocationName != "")
13873  {
13874  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
13875  {
13876  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
13877  // use reference so can change internals where necessary
13878  if((AVEntry2.Command == "fsp") || (AVEntry2.Command == "rsp"))
13879  {
13880  AVEntry2.LocationName = AVEntry.LocationName;
13881  } //test for any unnamed AtLoc entries at end of name setting
13882  else if(AVEntry2.LocationType != AtLocation)
13883  {
13884  break;
13885  }
13886  }
13887  }
13888  }
13889  }
13890 
13891 //check that all named or give error message
13892  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13893  {
13894  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13895  {
13896  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13897  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
13898  {
13899  if(AVEntry.LocationName == "")
13900  {
13901  SecondPassMessage(GiveMessages, "Error in timetable - an 'fsp' or 'rsp' event must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13902  TrainDataVector.clear();
13903  Utilities->CallLogPop(2617);
13904  return(false);
13905  }
13906  }
13907  }
13908  }
13909 
13910 //now set all Sfs entries from the above
13911  for(unsigned int x = 0; x < TrainDataVector.size(); x++) //at v2.15.0 set Sfs & Sns locations from corresponding fsp/rsp & Fns entries, shuttles ok as they are
13912  {
13913  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13914  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13915  // use reference so can change internals
13916  if(AVEntry0.Command == "Sfs")
13917  {
13918  //new at v2.15.0. Only set location if have a forward and backward linkage at same time (these all set when SecondPassActions called). This isn't
13919  //rigorous as may have more than one, but if do then will be caught below in CheckCrossReferencesAndSetData
13920  //If fail to find location just ignore as will be caught later in CheckCrossReferencesAndSetData
13921  //note that at this stage the OtherHeadCode values are service refs, as haven't yet been changed back to headcodes until StripExcessFromHeadCode called
13922  //at end of this function
13923  //need to be the same: forward & reverse service refs, event times, commands correspond
13924 
13925  //successor checks first: /no starts, /finishes except Frh & Fjo (as of v2.15.0), /repeats, /pas, /TimeTimeLoc or /TimeLoc arr; any other cmd or TimeLoc (dep) OK
13926  if(TDEntry.ActionVector.size() < 2)
13927  {
13928  SecondPassMessage(GiveMessages, "Error in timetable - insufficient actions follwing an 'Sfs' event for: " + TDEntry.HeadCode);
13929  TrainDataVector.clear();
13930  Utilities->CallLogPop(2587);
13931  return(false);
13932  }
13933  TActionVectorEntry AVEntry1 = TDEntry.ActionVector.at(1);
13934  if(!AtLocSuccessor(AVEntry1))
13935  {
13936  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' entry is followed by an illegal event for: " + TDEntry.HeadCode +
13937  ". The event isn't valid for a stationary train.");
13938  TrainDataVector.clear();
13939  Utilities->CallLogPop(2588);
13940  return(false);
13941  }
13942  if((AVEntry1.SequenceType == StartSequence) || ((AVEntry1.SequenceType == FinishSequence) && (AVEntry1.Command != "Frh") && (AVEntry1.Command != "Fjo")) ||
13943  (AVEntry1.FormatType == Repeat))
13944  {
13945  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' entry is followed by an illegal event for: " + TDEntry.HeadCode);
13946  TrainDataVector.clear();
13947  Utilities->CallLogPop(2589);
13948  return(false);
13949  }
13950 
13951  //now set the location and location type
13952  TDateTime SfsEventTime = AVEntry0.EventTime;
13953  AnsiString SfsServiceRef = TDEntry.ServiceReference;
13954  bool BreakFlag = false;
13955  for(unsigned int y = 0; y < TrainDataVector.size(); y++)
13956  {
13957  for(unsigned int z = 0; z < TrainDataVector.at(y).ActionVector.size(); z++)
13958  {
13959  if(((TrainDataVector.at(y).ActionVector.at(z).Command == "fsp") || (TrainDataVector.at(y).ActionVector.at(z).Command == "rsp")) &&
13960  (SfsEventTime == TrainDataVector.at(y).ActionVector.at(z).EventTime) && (TrainDataVector.at(y).HeadCode == AVEntry0.OtherHeadCode))
13961  { //forward linkage found
13962  if(TrainDataVector.at(y).ActionVector.at(z).OtherHeadCode == SfsServiceRef) //OtherHeadCode values are service refs, see above
13963  { //reverse linkage found
13964  AVEntry0.LocationName = TrainDataVector.at(y).ActionVector.at(z).LocationName;
13965  AVEntry0.LocationType = AtLocation;
13966  BreakFlag = true;
13967  break;
13968  }
13969  }
13970  }
13971  if(BreakFlag)
13972  {
13973  break;
13974  }
13975  } //test for any unnamed AtLoc entries at end of name setting
13976  }
13977  }
13978 
13979  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
13980  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13981  {
13982  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13983  {
13984  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13985  if(AVEntry.LocationName != "")
13986  {
13987  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
13988  {
13989  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
13990  // use reference so can change internals where necessary
13991  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
13992  {
13993  AVEntry2.LocationName = AVEntry.LocationName;
13994  } //test for any unnamed AtLoc entries at end of name setting
13995  else
13996  {
13997  break;
13998  }
13999  }
14000  }
14001  }
14002  }
14003  // all location names should now be set
14004 
14005 //now test for any unnamed AtLoc entries where Command != "" and give message if find any - shouldn't find any if above checks comprehensive
14006  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14007  {
14008  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14009  {
14010  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14011  if((AVEntry.LocationType == AtLocation) && (AVEntry.LocationName == "") && (AVEntry.Command != ""))
14012  {
14013  if((AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "cdt") || (AVEntry.Command == "dsc"))
14014  {
14015  SecondPassMessage(GiveMessages, "Error in timetable - '" + AVEntry.Command + "' must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
14016  TrainDataVector.clear();
14017  Utilities->CallLogPop(2619);
14018  return(false);
14019  }
14020  else if((AVEntry.Command == "Sns") || (AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns-fsh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Sns-sh"))
14021  {
14022  SecondPassMessage(GiveMessages, "Error in timetable - the location of the '" + AVEntry.Command + "' event in service " + TrainDataVector.at(x).ServiceReference + " can't be identified. " +
14023  "Please make sure that the finish event of the service that links to this event is preceded by an "
14024  "event at the same location that has an identified location name, normally an arrival.");
14025  TrainDataVector.clear();
14026  Utilities->CallLogPop(2620);
14027  return(false);
14028  }
14029  else if((AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"))
14030  {
14031  SecondPassMessage(GiveMessages, "Error in timetable - '" + AVEntry.Command + "must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
14032  TrainDataVector.clear();
14033  Utilities->CallLogPop(2621);
14034  return(false);
14035  }
14036  }
14037  }
14038  }
14039 
14040  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
14041  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14042  {
14043  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14044  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14045  {
14046  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14047  if((AVEntry.SequenceType == FinishSequence) && (AVEntry.Command != "F-nshs"))
14048  {
14049  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
14050  // i.e at least one more, must be a repeat
14051  {
14052  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
14053  {
14054  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
14055  TrainDataVector.clear();
14056  Utilities->CallLogPop(798);
14057  return(false);
14058  }
14059  }
14060  }
14061  if(AVEntry.Command == "F-nshs")
14062  {
14063  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
14064  // i.e has to be the last
14065  {
14066  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
14067  TrainDataVector.clear();
14068  Utilities->CallLogPop(1049);
14069  return(false);
14070  }
14071  }
14072  if(AVEntry.Command == "pas")
14073  {
14074  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14075  {
14076  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
14077  TrainDataVector.clear();
14078  Utilities->CallLogPop(1518);
14079  return(false);
14080  }
14081  }
14082  if(AVEntry.Command == "jbo")
14083  {
14084  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14085  {
14086  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
14087  TrainDataVector.clear();
14088  Utilities->CallLogPop(800);
14089  return(false);
14090  }
14091  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14092  if(!AtLocSuccessor(AVEntry2))
14093  {
14094  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
14095  ". The event isn't valid for a stationary train.");
14096  TrainDataVector.clear();
14097  Utilities->CallLogPop(801);
14098  return(false);
14099  }
14100  }
14101  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
14102  {
14103  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14104  {
14105  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
14106  TrainDataVector.clear();
14107  Utilities->CallLogPop(802);
14108  return(false);
14109  }
14110  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14111  if(!AtLocSuccessor(AVEntry2))
14112  {
14113  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
14114  ". The event isn't valid for a stationary train.");
14115  TrainDataVector.clear();
14116  Utilities->CallLogPop(803);
14117  return(false);
14118  }
14119  }
14120  if(AVEntry.Command == "cdt")
14121  {
14122  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14123  {
14124  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
14125  TrainDataVector.clear();
14126  Utilities->CallLogPop(804);
14127  return(false);
14128  }
14129  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14130  if(!AtLocSuccessor(AVEntry2))
14131  {
14132  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
14133  ". The event isn't valid for a stationary train.");
14134  TrainDataVector.clear();
14135  Utilities->CallLogPop(805);
14136  return(false);
14137  }
14138  }
14139  if(AVEntry.Command == "dsc")
14140  {
14141  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14142  {
14143  SecondPassMessage(GiveMessages, "Error in timetable - a 'dsc' can't be the last event for: " + TDEntry.HeadCode);
14144  TrainDataVector.clear();
14145  Utilities->CallLogPop(2602);
14146  return(false);
14147  }
14148  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14149  if(!AtLocSuccessor(AVEntry2))
14150  {
14151  SecondPassMessage(GiveMessages, "Error in timetable - a 'dsc' is followed by an illegal event for: " + TDEntry.HeadCode +
14152  ". The event isn't valid for a stationary train.");
14153  TrainDataVector.clear();
14154  Utilities->CallLogPop(2603);
14155  return(false);
14156  }
14157  }
14158  if(AVEntry.FormatType == TimeTimeLoc)
14159  {
14160  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14161  {
14162  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
14163  TrainDataVector.clear();
14164  Utilities->CallLogPop(806);
14165  return(false);
14166  }
14167  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14168  if(!MovingSuccessor(AVEntry2))
14169  {
14170  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
14171  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
14172  TrainDataVector.clear();
14173  Utilities->CallLogPop(807);
14174  return(false);
14175  }
14176  }
14177  if(AVEntry.FormatType == PassTime)
14178  {
14179  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14180  {
14181  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
14182  TrainDataVector.clear();
14183  Utilities->CallLogPop(1530);
14184  return(false);
14185  }
14186  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14187  if(!MovingSuccessor(AVEntry2))
14188  {
14189  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
14190  ". The event isn't valid for a moving train.");
14191  TrainDataVector.clear();
14192  Utilities->CallLogPop(1531);
14193  return(false);
14194  }
14195  }
14196  if(AVEntry.FormatType == Repeat)
14197  {
14198  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
14199  {
14200  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
14201  TrainDataVector.clear();
14202  Utilities->CallLogPop(808);
14203  return(false);
14204  }
14205  }
14206  }
14207  }
14208 
14209  // set arrival & departure times for TimeLocs & set their EventTimes to -1
14210  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14211  {
14212  bool LastEntryIsAnArrival = false;
14213  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14214  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
14215  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
14216  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
14217  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
14218  {
14219  LastEntryIsAnArrival = false;
14220  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14221  {
14222  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14223  if(AVEntry.FormatType == TimeLoc)
14224  {
14225  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
14226  {
14227  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
14228  }
14229  if(LastEntryIsAnArrival)
14230  {
14231  AVEntry.DepartureTime = AVEntry.EventTime;
14232  AVEntry.EventTime = TDateTime(-1);
14233  LastEntryIsAnArrival = false;
14234  }
14235  else // last entry a departure
14236  {
14237  AVEntry.ArrivalTime = AVEntry.EventTime;
14238  AVEntry.EventTime = TDateTime(-1);
14239  LastEntryIsAnArrival = true;
14240  }
14241  }
14242  }
14243  }
14244  else // all others stopped at beginning
14245  {
14246  LastEntryIsAnArrival = true;
14247  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14248  {
14249  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14250  if(AVEntry.FormatType == TimeLoc)
14251  {
14252  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
14253  {
14254  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
14255  }
14256  if(LastEntryIsAnArrival)
14257  {
14258  AVEntry.DepartureTime = AVEntry.EventTime;
14259  AVEntry.EventTime = TDateTime(-1);
14260  LastEntryIsAnArrival = false;
14261  }
14262  else // last entry a departure
14263  {
14264  AVEntry.ArrivalTime = AVEntry.EventTime;
14265  AVEntry.EventTime = TDateTime(-1);
14266  LastEntryIsAnArrival = true;
14267  }
14268  }
14269  }
14270  }
14271  }
14272  // perform remaining successor checks for TimeLocs
14273  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14274  {
14275  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14276  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14277  {
14278  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14279  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
14280  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
14281  {
14282  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14283  {
14284  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
14285  TrainDataVector.clear();
14286  Utilities->CallLogPop(809);
14287  return(false);
14288  }
14289  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14290  if(!AtLocSuccessor(AVEntry2))
14291  {
14292  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
14293  ". The event isn't valid for a stationary train.");
14294  TrainDataVector.clear();
14295  Utilities->CallLogPop(810);
14296  return(false);
14297  }
14298  }
14299  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
14300  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
14301  {
14302  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14303  {
14304  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
14305  TrainDataVector.clear();
14306  Utilities->CallLogPop(811);
14307  return(false);
14308  }
14309  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14310  if(!MovingSuccessor(AVEntry2))
14311  {
14312  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
14313  ". The event isn't valid for a moving train.");
14314  TrainDataVector.clear();
14315  Utilities->CallLogPop(812);
14316  return(false);
14317  }
14318  }
14319  }
14320  }
14321 
14322  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
14323  // & repeats have no times set
14324  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14325  {
14326  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14327  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14328  {
14329  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14330  if(AVEntry.FormatType == TimeLoc)
14331  {
14332  if(AVEntry.EventTime != TDateTime(-1))
14333  {
14334  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
14335  }
14336  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
14337  {
14338  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
14339  }
14340  }
14341  if(AVEntry.FormatType == TimeTimeLoc)
14342  {
14343  if(AVEntry.EventTime != TDateTime(-1))
14344  {
14345  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
14346  }
14347  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
14348  {
14349  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
14350  }
14351  }
14352  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
14353  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
14354  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
14355  {
14356  if(AVEntry.EventTime == TDateTime(-1))
14357  {
14358  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
14359  }
14360  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
14361  {
14362  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
14363  }
14364  }
14365  if(AVEntry.FormatType == Repeat)
14366  {
14367  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
14368  {
14369  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
14370  }
14371  }
14372  }
14373  }
14374 
14375  // check times stay same or increase, note that can have time of 0 if include midnight
14376  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14377  {
14378  TDateTime CurrentTime = TTClockTime; // the timetable start time
14379  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14380  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14381  {
14382  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14383  if(AVEntry.FormatType == Repeat)
14384  {
14385  break;
14386  }
14387  if(AVEntry.FormatType == FinRemHere)
14388  {
14389  break;
14390  }
14391  if(AVEntry.FormatType == TimeTimeLoc)
14392  {
14393  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
14394  {
14395  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
14396  TDEntry.HeadCode);
14397  TrainDataVector.clear();
14398  Utilities->CallLogPop(813);
14399  return(false);
14400  }
14401  if(AVEntry.ArrivalTime < CurrentTime)
14402  {
14403  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
14404  TDEntry.HeadCode);
14405  TrainDataVector.clear();
14406  Utilities->CallLogPop(814);
14407  return(false);
14408  }
14409  CurrentTime = AVEntry.DepartureTime;
14410  continue;
14411  }
14412  if(AVEntry.FormatType == TimeLoc)
14413  {
14414  if(AVEntry.ArrivalTime >= TDateTime(0))
14415  {
14416  if(AVEntry.ArrivalTime < CurrentTime)
14417  {
14418  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
14419  TrainDataVector.clear();
14420  Utilities->CallLogPop(815);
14421  return(false);
14422  }
14423  CurrentTime = AVEntry.ArrivalTime;
14424  }
14425  else
14426  {
14427  if(AVEntry.DepartureTime < CurrentTime)
14428  // both may be 0 legitimately so must allow for this
14429  {
14430  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
14431  TrainDataVector.clear();
14432  Utilities->CallLogPop(816);
14433  return(false);
14434  }
14435  CurrentTime = AVEntry.DepartureTime;
14436  }
14437  continue;
14438  }
14439  if(AVEntry.EventTime < CurrentTime)
14440  // all others have EventTime set
14441  {
14442  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
14443  ", may be before timetable start time");
14444  TrainDataVector.clear();
14445  Utilities->CallLogPop(835);
14446  return(false);
14447  }
14448  CurrentTime = AVEntry.EventTime;
14449  continue;
14450  }
14451  }
14452 
14453  // check locations consistent
14454  AnsiString LastLocationName = "";
14455 
14456  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14457  {
14458  bool LastEntryIsAnArrival = false;
14459  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14460  // first deal with moving Snt entries (all else stopped)
14461  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
14462  {
14463  LastEntryIsAnArrival = false;
14464  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
14465  if(LastLocationName != "")
14466  {
14467  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
14468  }
14469  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
14470  y++) // note that immediate successor to a moving Snt can only be a Moving type
14471  {
14472  // if it's a SignallerControl entry then the condition isn't met
14473  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14474  if(AVEntry.FormatType == Repeat)
14475  {
14476  break; // repeat = reached end (+allows repeat after signaller controlled entry)
14477  }
14478  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
14479  {
14480  if(AVEntry.LocationName != LastLocationName)
14481  {
14482  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14483  AVEntry.Command);
14484  TrainDataVector.clear();
14485  Utilities->CallLogPop(823);
14486  return(false);
14487  }
14488  }
14489  else if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdDescription))
14490  // cdt is the only TimeCmd & dsc is the only TimeCmdDescription
14491  {
14492  if(AVEntry.LocationName != LastLocationName)
14493  {
14494  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14495  AVEntry.Command);
14496  TrainDataVector.clear();
14497  Utilities->CallLogPop(824);
14498  return(false);
14499  }
14500  }
14501  else if(AVEntry.FormatType == TimeTimeLoc)
14502  {
14503  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
14504  // last entry must be a departure or would have failed earlier
14505  {
14506  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
14507  TwoLocationFlag = true;
14508 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
14509 // TwoOrMoreLocationsWarningGiven = true;
14510  }
14511  LastLocationName = AVEntry.LocationName;
14512  LastEntryIsAnArrival = false;
14513  }
14514  else if(AVEntry.FormatType == TimeLoc)
14515  {
14516  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
14517  {
14518  SecondPassMessage(GiveMessages,
14519  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
14520  TrainDataVector.clear();
14521  Utilities->CallLogPop(826);
14522  return(false);
14523  }
14524  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
14525  {
14526  SecondPassMessage(GiveMessages,
14527  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
14528  TrainDataVector.clear();
14529  Utilities->CallLogPop(827);
14530  return(false);
14531  }
14532  LastLocationName = AVEntry.LocationName;
14533  LastEntryIsAnArrival = !LastEntryIsAnArrival;
14534  }
14535  }
14536  }
14537  else // all stationary starting entries
14538  {
14539  LastEntryIsAnArrival = true;
14540  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
14541  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14542  {
14543  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14544  if(AVEntry.FormatType == Repeat)
14545  {
14546  break;
14547  }
14548  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
14549  // no need to add anything for shuttle starts since they are at loc (0) anyway
14550  {
14551  if(AVEntry.LocationName != LastLocationName)
14552  {
14553  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14554  AVEntry.Command);
14555  TrainDataVector.clear();
14556  Utilities->CallLogPop(828);
14557  return(false);
14558  }
14559  }
14560  else if(AVEntry.FormatType == TimeCmd)
14561  // cdt is the only TimeCmd
14562  {
14563  if(AVEntry.LocationName != LastLocationName)
14564  {
14565  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14566  AVEntry.Command);
14567  TrainDataVector.clear();
14568  Utilities->CallLogPop(829);
14569  return(false);
14570  }
14571  }
14572  else if(AVEntry.FormatType == TimeTimeLoc)
14573  {
14574  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
14575  // last entry must be a departure or would have failed earlier
14576  {
14577  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
14578  TwoLocationFlag = true;
14579 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
14580 // TwoOrMoreLocationsWarningGiven = true;
14581  }
14582  LastLocationName = AVEntry.LocationName;
14583  LastEntryIsAnArrival = false;
14584  }
14585  else if(AVEntry.FormatType == TimeLoc)
14586  {
14587  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
14588  {
14589  SecondPassMessage(GiveMessages,
14590  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
14591  TrainDataVector.clear();
14592  Utilities->CallLogPop(831);
14593  return(false);
14594  }
14595  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
14596  {
14597  SecondPassMessage(GiveMessages,
14598  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
14600 // TrainDataVector.clear();
14601 // Utilities->CallLogPop(832);
14602 // return false;
14603  }
14604  LastLocationName = AVEntry.LocationName;
14605  LastEntryIsAnArrival = !LastEntryIsAnArrival;
14606  }
14607  }
14608  }
14609  }
14610 
14611  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
14612  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
14613  AnsiString LocationNameToBeChecked = "";
14614 
14615  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14616  {
14617  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14618  unsigned int y = 0;
14619  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
14620  // first discard unlocated Snt entries as they don't have location name set
14621  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
14622  {
14623  y = 1;
14624  }
14625  while(y < TDEntry.ActionVector.size())
14626  // need to check each location name separately in turn, skipped for SignallerControl entries
14627  {
14628  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
14629  {
14630  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
14631  }
14632  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
14633  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
14634  {
14635  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
14636  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
14637  {
14638  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
14639  }
14640  if(AVEntry.Command == "cdt")
14641  {
14642  break; // out of the 'z' loop since the check is only valid up to a change of direction
14643  }
14644  if(AVEntry.LocationName == LocationNameToBeChecked)
14645  {
14646  continue; // keep going while name same
14647  }
14648  if(AVEntry.LocationName != LocationNameToBeChecked)
14649  // if name different check forwards to see if repeats
14650  {
14651  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
14652  {
14653  if(TDEntry.ActionVector.at(a).Command == "cdt")
14654  {
14655  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
14656  }
14657  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
14658  {
14659  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
14660  TwoLocationFlag = true;
14661 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
14662 // TwoOrMoreLocationsWarningGiven = true;
14663  }
14664  }
14665  break; // out of the 'z' loop since have checked 'a' as far as need to
14666  }
14667  }
14668  y++;
14669  }
14670  }
14671  if(TwoLocationFlag)
14672  {
14673  TwoLocationList.sort(); //need to sort first in alphabetical order to ensure all duplictes removed
14674  TwoLocationList.unique(); //remove duplicates
14675  }
14676 
14677  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
14678  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14679  {
14680  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14681  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14682  {
14683  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14684  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
14685  {
14686  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
14687  }
14688  AnsiString LocName = "";
14689  // dummy, only used so can call IsSNTEntryLocated
14690  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
14691  {
14692  if(AVEntry.LocationName == "")
14693  {
14694  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
14695  }
14696  }
14697  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
14698  {
14699  if(AVEntry.LocationName != "")
14700  {
14701  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
14702  }
14703  }
14704  }
14705  }
14706 
14707 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
14708  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
14709  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
14710 
14711  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
14712  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
14713 
14714  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
14715  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
14716  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
14717 */
14718  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
14719  {
14720  // non-shuttles & non-repeating links separately, but don't check that there isn't a
14721  // duplicate between a non-repeating shuttle and another - leave original tests in as
14722  // these also set the pointers
14723  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14724  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14725  {
14726  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14727  if(AVEntry.OtherHeadCode != "")
14728  {
14729  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
14730  {
14731  Utilities->CallLogPop(1584);
14732  return(false); // error message given in called function
14733  }
14734  }
14735  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
14736  {
14737  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
14738  {
14739  Utilities->CallLogPop(1585);
14740  return(false); // error message given in called function
14741  }
14742  }
14743  }
14744  }
14745 
14746  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14747  {
14748  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14749  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14750  {
14751  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14752  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
14753  {
14754  if(AVEntry.OtherHeadCode != "")
14755  {
14756  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, true, GiveMessages))
14757  // false = non-shuttle
14758  {
14759  Utilities->CallLogPop(864);
14760  return(false); // error message given in called function
14761  }
14762  }
14763  }
14764  }
14765  }
14766 
14767  // now repeat the check just for the shuttles
14768  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14769  {
14770  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14771  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14772  {
14773  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14774  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
14775  {
14776  if(AVEntry.OtherHeadCode != "")
14777  {
14778  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, true, GiveMessages))
14779  // true = shuttle
14780  {
14781  Utilities->CallLogPop(1100);
14782  return(false); // error message given in called function
14783  }
14784  }
14785  }
14786  }
14787  }
14788 
14789  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14790  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14791  {
14792  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14793  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14794  {
14795  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14796  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
14797  {
14798  if(!CheckNonRepeatingShuttleLinksAndSetData(0, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, true, GiveMessages))
14799  {
14800  Utilities->CallLogPop(1060);
14801  return(false); // error message given in called function
14802  }
14803  }
14804  }
14805  }
14806 
14807  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14808  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14809  // don't ever need to and as designed would skip repeats
14810  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14811  {
14812  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14813  {
14814  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14815  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
14816  {
14817  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
14818  {
14819  Utilities->CallLogPop(1090);
14820  return(false); // error message given in called function
14821  }
14822  }
14823  }
14824  }
14825 
14826  // check all entries have all types set to something
14827  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14828  {
14829  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14830  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14831  {
14832  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14833  if(AVEntry.FormatType == NoFormat)
14834  {
14835  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
14836  }
14837  else if(AVEntry.SequenceType == NoSequence)
14838  {
14839  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
14840  }
14841  else if(AVEntry.LocationType == NoLocation)
14842  {
14843  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
14844  }
14845  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
14846  {
14847  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
14848  }
14849  }
14850  }
14851 
14852  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
14853  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14854  {
14855  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14856  // non-const reference so can alter content
14857  TTrainOperatingData TData;
14858  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
14859  if(LastAVEntry.FormatType == Repeat) // check if a repeat
14860  {
14861 /*
14862  class TTrainOperatingData
14863  {
14864  public:
14865  int TrainID; - default, set at construction
14866  TActionEventType EventReported; used during operation
14867  TRunningEntry RunningEntry; - default, set at construction
14868  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
14869  };
14870 */
14871  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
14872  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
14873  {
14874  TDEntry.TrainOperatingDataVector.push_back(TData);
14875  }
14876  }
14877  else
14878  {
14879  TDEntry.NumberOfTrains = 1;
14880  }
14881  }
14882 
14883  // check that don't include any Continuation names
14884  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14885  {
14886  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14887  {
14888  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
14889  AnsiString HC = TrainDataVector.at(x).HeadCode;
14890  if(LocName != "")
14891  {
14892  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
14893  {
14894  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
14895  TrainDataVector.clear();
14896  Utilities->CallLogPop(1578);
14897  return(false);
14898  }
14899  }
14900  }
14901  }
14902 
14903  // check that all repeat times below 96h
14904  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14905  {
14906  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
14907  int IncMinutes = 0;
14908  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
14909  {
14910  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
14911  }
14912  else
14913  {
14914  continue; // basic times already checked in CheckTimeValidity
14915  }
14916  AnsiString HC = TrainDataVector.at(x).HeadCode;
14917  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14918  {
14919  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
14920  {
14921  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14922  {
14923  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
14924  TrainDataVector.clear();
14925  Utilities->CallLogPop(1818);
14926  return(false);
14927  }
14928  }
14929  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
14930  {
14931  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14932  // 3d 23h 59m = 3.9993055556
14933  {
14934  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
14935  TrainDataVector.clear();
14936  Utilities->CallLogPop(1819);
14937  return(false);
14938  }
14939  }
14940  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
14941  {
14942  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14943  // 3d 23h 59m = 3.9993055556
14944  {
14945  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
14946  TrainDataVector.clear();
14947  Utilities->CallLogPop(1820);
14948  return(false);
14949  }
14950  }
14951  }
14952  }
14953 
14954  // Now that all set up change any extended headcodes back to ordinary headcodes
14955  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14956  {
14957  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
14958  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14959  {
14960  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
14961  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
14962  }
14963  }
14964 
14965  // SaveTrainDataVectorToFile(0);//for testing purposes
14967  Utilities->CallLogPop(782);
14968  return(true);
14969 }
14970 
14971 // ---------------------------------------------------------------------------
14972 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
14974 {
14975  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
14976 }
14977 
14978 // ---------------------------------------------------------------------------
14979 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/dsc/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
14981 {
14982  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
14983  (AVEntry.Command == "cdt") || (AVEntry.Command == "dsc") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
14984  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
14985 }
14986 
14987 // ---------------------------------------------------------------------------
14988 
14989 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
14990 {
14991  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
14992  if(HeadCode.Length() > 4) // ignore otherwise
14993  {
14994  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
14995  }
14996  Utilities->CallLogPop(1593);
14997 }
14998 
14999 // ---------------------------------------------------------------------------
15000 
15001 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
15002 {
15003  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
15004  SecondHeadCode);
15005  int ForwardCount = 0;
15006  int ReverseCount = 0;
15007 
15008  if(MainHeadCode == SecondHeadCode)
15009  {
15010  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
15011  TrainDataVector.clear();
15012  Utilities->CallLogPop(1594);
15013  return(false);
15014  }
15015  // forward check
15016  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15017  {
15018  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15019  if(TDEntry.HeadCode == MainHeadCode)
15020  {
15021  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15022  {
15023  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15024  if(AVEntry.OtherHeadCode == SecondHeadCode)
15025  {
15026  ForwardCount++;
15027  }
15028  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
15029  // need own check in case both 'Other' & 'NonRepeating' have same headcode
15030  {
15031  ForwardCount++;
15032  }
15033  }
15034  }
15035  }
15036  if(ForwardCount == 0)
15037  // this is an exception because the headcodes are selected in the same order as the forward check
15038  {
15039  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
15040  }
15041  if(ForwardCount > 2)
15042  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
15043  {
15044  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
15045  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
15046  TrainDataVector.clear();
15047  Utilities->CallLogPop(1587);
15048  return(false);
15049  }
15050  // reverse check
15051  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15052  {
15053  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15054  if(TDEntry.HeadCode == SecondHeadCode)
15055  {
15056  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15057  {
15058  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15059  if(AVEntry.OtherHeadCode == MainHeadCode)
15060  {
15061  ReverseCount++;
15062  }
15063  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
15064  {
15065  ReverseCount++;
15066  }
15067  }
15068  }
15069  }
15070 
15071  if(ReverseCount == 0)
15072  {
15073  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
15074  TrainDataVector.clear();
15075  Utilities->CallLogPop(1588);
15076  return(false);
15077  }
15078  if(ReverseCount > 2)
15079  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
15080  {
15081  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
15082  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
15083  TrainDataVector.clear();
15084  Utilities->CallLogPop(1589);
15085  return(false);
15086  }
15087  if(ForwardCount != ReverseCount)
15088  {
15089  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
15090  " than the other way round");
15091  TrainDataVector.clear();
15092  Utilities->CallLogPop(1610);
15093  return(false);
15094  }
15095  Utilities->CallLogPop(1590);
15096  return(true);
15097 }
15098 
15099 // ---------------------------------------------------------------------------
15100 
15101 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool SetDataAndCheckLocations, bool GiveMessages)
15102 /* Return false for no find or more than one find, check correct types of link
15103  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
15104  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
15105  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
15106  Then do the same in reverse.
15107  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
15108  if main is Fns other must be Sns; if main is jbo other must be Fjo.
15109  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
15110  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
15111  for Sfs & Sns services. Finally check the repeat entries if present are consistent
15112 
15113  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
15114  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
15115 
15116  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
15117 */
15118 
15119 {
15120  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
15121  int ForwardCount = 0;
15122  int ReverseCount = 0;
15123  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
15124  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
15125  TTrainDataEntry *MainTrainDataPtr = 0;
15126  TTrainDataEntry *OtherTrainDataPtr = 0;
15127 
15128  // forward check
15129  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15130  {
15131  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15132  if(TDEntry.HeadCode == MainHeadCode)
15133  {
15134  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15135  {
15136  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15137  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
15138  {
15139  if(AVEntry.OtherHeadCode == OtherHeadCode)
15140  {
15141  MainTrainDataPtr = &TrainDataVector.at(x);
15142  ForwardEntryPtr = &AVEntry;
15143  ForwardCount++;
15144  ForwardTDVectorNumber = x;
15145  }
15146  }
15147  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
15148  (AVEntry.Command == "Frh-sh")))
15149  {
15150  if(AVEntry.OtherHeadCode == OtherHeadCode)
15151  {
15152  MainTrainDataPtr = &TrainDataVector.at(x);
15153  ForwardEntryPtr = &AVEntry;
15154  ForwardCount++;
15155  ForwardTDVectorNumber = x;
15156  }
15157  }
15158  }
15159  }
15160  }
15161  if(ForwardCount == 0)
15162  // this is an exception because the headcodes are selected in the same order as the forward check
15163  {
15164  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
15165  }
15166  if(ForwardCount > 1)
15167  {
15168  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
15169  MainHeadCode);
15170  TrainDataVector.clear();
15171  Utilities->CallLogPop(836);
15172  return(false);
15173  }
15174  // reverse check
15175  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15176  {
15177  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15178  if(TDEntry.HeadCode == OtherHeadCode)
15179  {
15180  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15181  {
15182  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15183  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
15184  {
15185  if(AVEntry.OtherHeadCode == MainHeadCode)
15186  {
15187  OtherTrainDataPtr = &TrainDataVector.at(x);
15188  ReverseCount++;
15189  ReverseEntryPtr = &AVEntry;
15190  ReverseTDVectorNumber = x;
15191  }
15192  }
15193  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
15194  {
15195  if(AVEntry.OtherHeadCode == MainHeadCode)
15196  {
15197  OtherTrainDataPtr = &TrainDataVector.at(x);
15198  ReverseCount++;
15199  ReverseEntryPtr = &AVEntry;
15200  ReverseTDVectorNumber = x;
15201  }
15202  }
15203  }
15204  }
15205  }
15206 
15207  if(ReverseCount == 0)
15208  {
15209  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
15210  TrainDataVector.clear();
15211  Utilities->CallLogPop(837);
15212  return(false);
15213  }
15214  if(ReverseCount > 1)
15215  {
15216  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
15217  OtherHeadCode);
15218  TrainDataVector.clear();
15219  Utilities->CallLogPop(838);
15220  return(false);
15221  }
15222  // these will all be false for !Shuttle
15223  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
15224  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
15225  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
15226  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
15227 
15228  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
15229  {
15230  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
15231  TrainDataVector.clear();
15232  Utilities->CallLogPop(1058);
15233  return(false);
15234  }
15235  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
15236  {
15237  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
15238  TrainDataVector.clear();
15239  Utilities->CallLogPop(1059);
15240  return(false);
15241  }
15242  if(SetDataAndCheckLocations)
15243  {
15244  if(ForwardEntryPtr->LocationName == "")
15245  {
15246  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
15247  ". One or other service does not have a location set");
15248  TrainDataVector.clear();
15249  Utilities->CallLogPop(526);
15250  return(false);
15251  }
15252  if(ReverseEntryPtr->LocationName == "")
15253  {
15254  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
15255  ". One or other service does not have a location set");
15256  TrainDataVector.clear();
15257  Utilities->CallLogPop(527);
15258  return(false);
15259  }
15260  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
15261  {
15262  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
15263  " is at a different location to the referencing train " + MainHeadCode);
15264  TrainDataVector.clear();
15265  Utilities->CallLogPop(842);
15266  return(false);
15267  }
15268  }
15269  // ignore shuttle repeat links for first time check
15270  if(!Shuttle)
15271  {
15272  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
15273  {
15274  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
15275  " has a different event time to the referencing train " + MainHeadCode);
15276  TrainDataVector.clear();
15277  Utilities->CallLogPop(525);
15278  return(false);
15279  }
15280  }
15281  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
15282  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
15283  if(ForwardShuttleStart && ReverseShuttleFinish)
15284  // Shuttle must be true if these are true
15285  {
15286  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
15287  {
15288  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
15289  " first repeat restart time not consistent with finish service " + OtherHeadCode);
15290  TrainDataVector.clear();
15291  Utilities->CallLogPop(1055);
15292  return(false);
15293  }
15294  }
15295  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
15296  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
15297  {
15298  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15299  {
15300  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
15301  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
15302  TrainDataVector.clear();
15303  Utilities->CallLogPop(528);
15304  return(false);
15305  }
15306  }
15307  if(ReverseEntryPtr->Command == "Fjo")
15308  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
15309  {
15310  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15311  {
15312  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
15313  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
15314  TrainDataVector.clear();
15315  Utilities->CallLogPop(862);
15316  return(false);
15317  }
15318  }
15319  if(ReverseEntryPtr->Command == "Fns")
15320  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
15321  {
15322  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15323  {
15324  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
15325  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
15326  TrainDataVector.clear();
15327  Utilities->CallLogPop(529);
15328  return(false);
15329  }
15330  }
15331  if(ForwardEntryPtr->Command == "Sfs")
15332  {
15333  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
15334  {
15335  SecondPassMessage(GiveMessages,
15336  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
15337  MainHeadCode);
15338  TrainDataVector.clear();
15339  Utilities->CallLogPop(530);
15340  return(false);
15341  }
15342  }
15343  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
15344  {
15345  if(ReverseEntryPtr->Command != "Sfs")
15346  {
15347  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
15348  MainHeadCode);
15349  TrainDataVector.clear();
15350  Utilities->CallLogPop(839);
15351  return(false);
15352  }
15353  else
15354  {
15355  if(SetDataAndCheckLocations)
15356  {
15357  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
15358  {
15359  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
15360  TrainDataVector.clear();
15361  Utilities->CallLogPop(849);
15362  return(false);
15363  }
15364  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
15365  {
15366  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
15367  TrainDataVector.clear();
15368  Utilities->CallLogPop(850);
15369  return(false);
15370  }
15371  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
15372  {
15373  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
15374  TrainDataVector.clear();
15375  Utilities->CallLogPop(846);
15376  return(false);
15377  }
15378  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15379  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15380  if(OtherTrainDataPtr->Description == "")
15381  {
15382  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
15383  }
15384  }
15385  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
15386  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15387  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15388  }
15389  }
15390  if(ForwardEntryPtr->Command == "Sns")
15391  {
15392  if(ReverseEntryPtr->Command != "Fns")
15393  {
15394  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
15395  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
15396  TrainDataVector.clear();
15397  Utilities->CallLogPop(531);
15398  return(false);
15399  }
15400  }
15401  if(ForwardEntryPtr->Command == "Fns")
15402  {
15403  if(ReverseEntryPtr->Command != "Sns")
15404  {
15405  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
15406  " and forms a new service with headcode " + OtherHeadCode);
15407  TrainDataVector.clear();
15408  Utilities->CallLogPop(840);
15409  return(false);
15410  }
15411  else
15412  {
15413  if(SetDataAndCheckLocations)
15414  {
15415  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15416  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15417  if(OtherTrainDataPtr->Description == "")
15418  {
15419  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
15420  }
15421  }
15422  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15423  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15424  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15425  }
15426  }
15427  if(ForwardEntryPtr->Command == "jbo")
15428  {
15429  if(ReverseEntryPtr->Command != "Fjo")
15430  {
15431  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
15432  " and is joined by a train with headcode " + OtherHeadCode);
15433  TrainDataVector.clear();
15434  Utilities->CallLogPop(841);
15435  return(false);
15436  }
15437  }
15438  if(ForwardEntryPtr->Command == "Fjo")
15439  {
15440  if(ReverseEntryPtr->Command != "jbo")
15441  {
15442  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
15443  " and joins a train with headcode " + OtherHeadCode);
15444  TrainDataVector.clear();
15445  Utilities->CallLogPop(532);
15446  return(false);
15447  }
15448  else
15449  {
15450  if(SetDataAndCheckLocations)
15451  {
15452  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15453  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15454  }
15455 /*
15456  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
15457  {
15458  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed; //this should only be set when the trains join
15459  } not when internal timetable being compiled. Dropped at v2.15.0 when Brent Mackie reported error on 18/02/23 via discord
15460 
15461  //added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a
15462  //zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
15463  //notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the
15464  //'joined by' train's max speed is less.
15465 */
15466  }
15467  }
15468  if(ForwardShuttleStart)
15469  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
15470  {
15471  if(!ReverseShuttleFinish)
15472  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
15473  {
15474  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
15475  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
15476  TrainDataVector.clear();
15477  Utilities->CallLogPop(1056);
15478  return(false);
15479  }
15480  }
15481  if(ReverseShuttleStart)
15482  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
15483  {
15484  if(!ForwardShuttleFinish)
15485  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
15486  {
15487  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
15488  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
15489  TrainDataVector.clear();
15490  Utilities->CallLogPop(1057);
15491  return(false);
15492  }
15493  else
15494  {
15495  if(SetDataAndCheckLocations)
15496  {
15497  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15498  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15499  }
15500 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
15501  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
15502 */
15503  }
15504  }
15505  // check repeat information consistent if present
15506  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
15507  // and those not accessed here
15508 
15509  // still need to check the non-repeating links and that they have no repeats - do that outside this function
15510  bool MainRepeat = false, OtherRepeat = false;
15511  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
15512 
15513  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
15514  {
15515  MainRepeat = true;
15516  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
15517  }
15518  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
15519  {
15520  OtherRepeat = true;
15521  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
15522  }
15523  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
15524  {
15525  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
15526  " and the associated train with headcode " + OtherHeadCode);
15527  TrainDataVector.clear();
15528  Utilities->CallLogPop(844);
15529  return(false);
15530  }
15531  if(MainRepeat && OtherRepeat)
15532  {
15533  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
15534  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
15535  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
15536  {
15537  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
15538  " and the associated train with headcode " + OtherHeadCode);
15539  TrainDataVector.clear();
15540  Utilities->CallLogPop(845);
15541  return(false);
15542  }
15543  }
15544  Utilities->CallLogPop(863);
15545  return(true);
15546 }
15547 
15548 // ---------------------------------------------------------------------------
15549 
15550 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
15551 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
15552 {
15553  // strip spaces from extreme ends of input
15554  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
15555  if(Input == "")
15556  {
15557  Utilities->CallLogPop(856);
15558  return;
15559  }
15560  while(Input[1] == ' ')
15561  {
15562  if(Input.Length() > 1)
15563  {
15564  Input = Input.SubString(2, Input.Length() - 1);
15565  }
15566  else
15567  {
15568  Input = "";
15569  Utilities->CallLogPop(857);
15570  return;
15571  }
15572  }
15573  if(Input == "")
15574  {
15575  Utilities->CallLogPop(858);
15576  return;
15577  }
15578  while(Input[Input.Length()] == ' ')
15579  {
15580  if(Input.Length() > 1)
15581  {
15582  Input = Input.SubString(1, Input.Length() - 1);
15583  }
15584  else
15585  {
15586  Input = "";
15587  Utilities->CallLogPop(859);
15588  return;
15589  }
15590  }
15591  // now strip spaces immediately after all commas and semicolons within the text
15592  AnsiString Output = "";
15593  bool DelimiterFound = false;
15594 
15595  for(int x = 1; x < Input.Length() + 1; x++)
15596  {
15597  if(DelimiterFound)
15598  {
15599  if(Input[x] == ' ')
15600  {
15601  continue;
15602  }
15603  }
15604  if((Input[x] != ',') && (Input[x] != ';'))
15605  {
15606  DelimiterFound = false;
15607  Output = Output + Input[x];
15608  }
15609  else
15610  {
15611  DelimiterFound = true;
15612  Output = Output + Input[x];
15613  }
15614  }
15615  if(Output == "")
15616  {
15617  Input = "";
15618  Utilities->CallLogPop(860);
15619  return;
15620  }
15621  // now strip spaces immediately before all commas and semicolons within the text
15622  Input = Output;
15623  Output = "";
15624  DelimiterFound = false;
15625  for(int x = Input.Length(); x > 0; x--)
15626  {
15627  if(DelimiterFound)
15628  {
15629  if(Input[x] == ' ')
15630  {
15631  continue;
15632  }
15633  }
15634  if((Input[x] != ',') && (Input[x] != ';'))
15635  {
15636  DelimiterFound = false;
15637  Output = AnsiString(Input[x]) + Output;
15638  }
15639  else
15640  {
15641  DelimiterFound = true;
15642  Output = AnsiString(Input[x]) + Output;
15643  }
15644  }
15645  Input = Output;
15646  Utilities->CallLogPop(861);
15647 }
15648 
15649 // ---------------------------------------------------------------------------
15650 
15651 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName) //this is the original version re-instated at v2.15.0 Beta6
15652 // checks if an Snt or Snt-sh entry with zero starting speed is followed (somewhere, not necessarily immediately) by a TimeLoc & has the same LocationName
15653 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
15654 // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
15655 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
15656 // are done in this function, they must be done elsewhere.
15657 //a starting speed > 0 always returns false
15658 {
15659  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
15660  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
15661  LocationName = "";
15662  if(TDEntry.StartSpeed > 0)
15663  {
15664  Utilities->CallLogPop(1784);
15665  return(false);
15666  }
15667  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
15668  {
15669  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
15670  }
15672  {
15673  Utilities->CallLogPop(852);
15674  return(false);
15675  }
15676  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
15677  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
15678 
15679  if(LocRear != "")
15680  {
15681  LocationName = LocRear;
15682  }
15683  else
15684  {
15685  LocationName = LocFront;
15686  }
15687  if(LocationName == "")
15688  {
15689  Utilities->CallLogPop(1036);
15690  return(false);
15691  }
15692  if(AVEntry0.SignallerControl)
15693  {
15694  Utilities->CallLogPop(1773);
15695  return(true);
15696  }
15697 // here if not a signaller start entry so must be at least one more entry, and it must be at the same location as the Snt to be located
15698 
15699 //Ok Not ok continue
15700 
15701 //Frh if Snt Frh-sh cdt
15702 //Fns if Snt Fns-sh fsp or rsp
15703 //Fjo if Snt TimeTimeLoc jbo
15704 //F-nshs if Snt pas dsc
15705 //TimeLoc dep Fer
15706 // TimeLoc arr
15707 
15708  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
15709  {
15710  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
15711  if(((AVEntry.Command == "Frh") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "F-nshs") || (AVEntry.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
15712  {
15713  Utilities->CallLogPop(1037);
15714  return(true);
15715  }
15716  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName == LocationName)) //will be a departure if same name- times not set yet so can't use them to confirm
15717  {
15718  Utilities->CallLogPop(2442);
15719  return(true);
15720  }
15721  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName != LocationName)) //arrival, not located
15722  {
15723  Utilities->CallLogPop(2438);
15724  return(false);
15725  }
15726  if((AVEntry.Command == "Fer") || (AVEntry.Command == "pas") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh") || (AVEntry.FormatType == TimeTimeLoc))
15727  {
15728  Utilities->CallLogPop(854);
15729  return(false);
15730  }
15731  if((AVEntry.Command == "cdt") || (AVEntry.Command == "dsc") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "jbo"))
15732  {
15733  continue;
15734  }
15735  }
15736  Utilities->CallLogPop(855);
15737  return(false);
15738 
15739 }
15740 
15741 // ---------------------------------------------------------------------------
15742 
15743 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
15744 {
15745  // checks that the new train start elements are valid - both exist & are connected, and that not
15746  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
15747  // & not starting with front on a continuation
15748  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
15749  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
15750 
15751  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
15752  if(RearPosition < 0)
15753  // error message given in GetTrackVectorPositionFromString
15754  {
15755  Utilities->CallLogPop(759);
15756  return(false);
15757  }
15758  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
15759  if(FrontPosition < 0)
15760  // error message given in GetTrackVectorPositionFromString
15761  {
15762  Utilities->CallLogPop(760);
15763  return(false);
15764  }
15765  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
15766  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
15767  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
15768 
15769  // check front & rear connected
15770  for(int x = 0; x < 4; x++)
15771  {
15772  if(RearTrackElement.Conn[x] == FrontPosition)
15773  {
15774  RearExitPos = x;
15775  break;
15776  }
15777  if(x == 3)
15778  {
15779  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
15780  Utilities->CallLogPop(762);
15781  return(false);
15782  }
15783  }
15784  // check not starting with front on a continuation
15785  if(FrontType == Continuation)
15786  {
15787  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
15788  Utilities->CallLogPop(937);
15789  return(false);
15790  }
15791  // check not starting on a level crossing
15792  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
15793  {
15794  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
15795  Utilities->CallLogPop(1951);
15796  return(false);
15797  }
15798  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
15799  {
15800  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
15801  Utilities->CallLogPop(1952);
15802  return(false);
15803  }
15804  // check if trying to start on diverging leg of points
15805  if((RearType == Points) && (RearExitPos == 3))
15806  {
15807  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
15808  Utilities->CallLogPop(936);
15809  return(false);
15810  }
15811  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
15812  {
15813  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
15814  Utilities->CallLogPop(1808);
15815  return(false);
15816  }
15817  Utilities->CallLogPop(905);
15818  return(true);
15819 }
15820 
15821 // ---------------------------------------------------------------------------
15822 
15823 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
15824 // Rear & front element validity already checked in CheckStartPositionValidity
15825 // This checks for points in correct orientation, no train at start position and not starting on a locked route
15826 {
15827  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
15828  AnsiString(RearExitPos));
15829  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
15830 
15831  if(RearTrackElement.TrackType == Continuation)
15832  {
15833  EventType = FailTrainEntry;
15834  }
15835  else
15836  {
15837  EventType = FailCreateTrain;
15838  }
15839  int FrontPosition = RearTrackElement.Conn[RearExitPos];
15840  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
15841  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
15842  TTrackType RearType = RearTrackElement.TrackType;
15843  TTrackType FrontType = FrontTrackElement.TrackType;
15844  AnsiString RearName, FrontName;
15845 
15846  if(RearTrackElement.ActiveTrackElementName != "")
15847  {
15848  RearName = RearTrackElement.ActiveTrackElementName;
15849  }
15850  else
15851  {
15852  RearName = RearTrackElement.ElementID;
15853  }
15854  if(FrontTrackElement.ActiveTrackElementName != "")
15855  {
15856  FrontName = FrontTrackElement.ActiveTrackElementName;
15857  }
15858  else
15859  {
15860  FrontName = FrontTrackElement.ElementID;
15861  }
15862  TPrefDirElement PrefDirElement; // needed for next function but not used
15863  int LockedVectorNumber; // needed for next function but not used
15864 
15865  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
15866  {
15867  if(ReportFlag)
15868  {
15869  if(EventType == FailCreateTrain)
15870  {
15871  EventType = FailCreateLockedRoute;
15872  }
15873  else
15874  {
15875  EventType = FailEnterLockedRoute;
15876  }
15877  LogActionError(47, HeadCode, "", EventType, FrontName);
15878  }
15879  Utilities->CallLogPop(940);
15880  return(false);
15881  }
15882  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
15883  {
15884  if(ReportFlag)
15885  {
15886  if(EventType == FailCreateTrain)
15887  {
15888  EventType = FailCreateLockedRoute;
15889  }
15890  else
15891  {
15892  EventType = FailEnterLockedRoute;
15893  }
15894  LogActionError(48, HeadCode, "", EventType, RearName);
15895  }
15896  Utilities->CallLogPop(1809);
15897  return(false);
15898  }
15899  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
15900  {
15901  if(ReportFlag)
15902  {
15903  LogActionError(27, HeadCode, "", EventType, RearName);
15904  }
15905  Utilities->CallLogPop(1810);
15906  return(false);
15907  }
15908  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
15909  {
15910  if(ReportFlag)
15911  {
15912  if(EventType == FailCreateTrain)
15913  {
15914  LogActionError(28, HeadCode, "", EventType, FrontName);
15915  }
15916  else
15917  {
15918  LogActionError(43, HeadCode, "", EventType, RearName);
15919  }
15920  }
15921  Utilities->CallLogPop(941);
15922  return(false);
15923  }
15924  if(RearType == Bridge)
15925  {
15926  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
15927  {
15928  if(ReportFlag)
15929  {
15930  LogActionError(29, HeadCode, "", EventType, RearName);
15931  }
15932  Utilities->CallLogPop(942);
15933  return(false);
15934  }
15935  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
15936  {
15937  if(ReportFlag)
15938  {
15939  LogActionError(30, HeadCode, "", EventType, RearName);
15940  }
15941  Utilities->CallLogPop(943);
15942  return(false);
15943  }
15944  }
15945  if(FrontType == Bridge)
15946  {
15947  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
15948  {
15949  if(ReportFlag)
15950  {
15951  if(EventType == FailCreateTrain)
15952  {
15953  LogActionError(31, HeadCode, "", EventType, FrontName);
15954  }
15955  else
15956  {
15957  LogActionError(44, HeadCode, "", EventType, RearName);
15958  }
15959  }
15960  Utilities->CallLogPop(944);
15961  return(false);
15962  }
15963  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
15964  {
15965  if(ReportFlag)
15966  {
15967  if(EventType == FailCreateTrain)
15968  {
15969  LogActionError(45, HeadCode, "", EventType, FrontName);
15970  }
15971  else
15972  {
15973  LogActionError(46, HeadCode, "", EventType, RearName);
15974  }
15975  }
15976  Utilities->CallLogPop(945);
15977  return(false);
15978  }
15979  }
15980  EventType = FailCreatePoints;
15981  if(RearType == Points)
15982  {
15983  if(RearTrackElement.Attribute == 1)
15984  {
15985  if(ReportFlag)
15986  {
15987  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
15988  }
15989  Utilities->CallLogPop(933);
15990  return(false);
15991  }
15992  }
15993  if(FrontType == Points)
15994  {
15995  if(FrontTrackElement.Attribute == 1)
15996  {
15997  if(ReportFlag)
15998  {
15999  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
16000  }
16001  Utilities->CallLogPop(934);
16002  return(false);
16003  }
16004  }
16005 
16006  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
16007  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
16008  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
16009  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
16010  int RouteNumber; //not used
16011  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
16012  {
16013  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
16014  {
16015  EventType = FailEntryRouteSetAgainst;
16016  if(ReportFlag)
16017  {
16018  LogActionError(63, HeadCode, "", EventType, RearName);
16019  }
16020  Utilities->CallLogPop(2317);
16021  return(false);
16022  }
16023  }
16024  Utilities->CallLogPop(939);
16025  return(true);
16026 }
16027 
16028 // ---------------------------------------------------------------------------
16029 
16030 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
16031 {
16032  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
16033  "," + AnsiString(IncDigits));
16034  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
16035  {
16036  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
16037  }
16038  if(!Last2CharactersBothDigits(2, BaseHeadCode))
16039  {
16040  Utilities->CallLogPop(1893);
16041  return(BaseHeadCode);
16042  }
16043  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
16044  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
16045 
16046  while(NextRepeatDigits >= 100)
16047  {
16048  NextRepeatDigits -= 100; // rolls over after 99
16049  }
16050  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
16051 
16052  if(NextRepeatDigitsStr.Length() < 2)
16053  {
16054  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
16055  }
16056  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
16057 
16058  Utilities->CallLogPop(1365);
16059  return(NextRepeatHeadCode);
16060 }
16061 
16062 // ---------------------------------------------------------------------------
16063 
16064 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
16065 {
16066  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
16067  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
16068  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
16069  Utilities->CallLogPop(1366);
16070  return(NextRepeatTime);
16071 }
16072 
16073 // ---------------------------------------------------------------------------
16074 
16075 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
16076 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
16077 {
16078  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
16079  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
16080  int ForwardSecs = int(double(ForwardEventTime) * 86400);
16081  int ReverseSecs = int(double(ReverseEventTime) * 86400);
16082  int RepeatSecs = RepeatMinutes * 60;
16083 
16084  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
16085  {
16086  Utilities->CallLogPop(1367);
16087  return(false);
16088  }
16089  else
16090  {
16091  Utilities->CallLogPop(1368);
16092  return(true);
16093  }
16094 }
16095 
16096 // ---------------------------------------------------------------------------
16097 
16098 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool SetDataAndCheckLocations, bool GiveMessages)
16099 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent, set links if SetDataAndCheckLocations true
16100 
16101 /* Double crosslink (shuttle) table:
16102 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
16103  Code ShuttleLink- EntryPtr ShuttleLink-
16104  HeadCode EntryPtr
16105 
16106 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
16107 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
16108 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
16109 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
16110 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
16111 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
16112 
16113 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
16114 */
16115 
16116 {
16117  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
16118  NonRepeatingHeadCode);
16119  int ForwardCount = 0;
16120  int ReverseCount = 0;
16121  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
16122  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
16123  // Forward corresponds to Main, Reverse to Other
16124  TTrainDataEntry *MainTrainDataPtr = 0;
16125  TTrainDataEntry *OtherTrainDataPtr = 0;
16126 
16127  // forward check
16128  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16129  {
16130  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
16131  if(TDEntry.HeadCode == MainHeadCode)
16132  {
16133  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
16134  {
16135  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
16136  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
16137  {
16138  MainTrainDataPtr = &TrainDataVector.at(x);
16139  ForwardEntryPtr = &AVEntry;
16140  ForwardCount++;
16141  ForwardTDVectorNumber = x;
16142  }
16143  }
16144  }
16145  }
16146  if(ForwardCount == 0)
16147  // this is an exception because the headcodes are selected in the same order as the forward check
16148  {
16149  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
16150  }
16151  if(ForwardCount > 1)
16152  {
16153  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
16154  MainHeadCode);
16155  TrainDataVector.clear();
16156  Utilities->CallLogPop(1061);
16157  return(false);
16158  }
16159  // reverse check
16160  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16161  {
16162  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
16163  if(TDEntry.HeadCode == NonRepeatingHeadCode)
16164  {
16165  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
16166  {
16167  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
16168  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
16169  {
16170  OtherTrainDataPtr = &TrainDataVector.at(x);
16171  ReverseCount++;
16172  ReverseEntryPtr = &AVEntry;
16173  ReverseTDVectorNumber = x;
16174  }
16175  }
16176  }
16177  }
16178 
16179  if(ReverseCount == 0)
16180  {
16181  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
16182  TrainDataVector.clear();
16183  Utilities->CallLogPop(1062);
16184  return(false);
16185  }
16186  if(ReverseCount > 1)
16187  {
16188  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
16189  NonRepeatingHeadCode);
16190  TrainDataVector.clear();
16191  Utilities->CallLogPop(1063);
16192  return(false);
16193  }
16194  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
16195  {
16196  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
16197  TrainDataVector.clear();
16198  Utilities->CallLogPop(1064);
16199  return(false);
16200  }
16201  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
16202  {
16203  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
16204  TrainDataVector.clear();
16205  Utilities->CallLogPop(1065);
16206  return(false);
16207  }
16208  if(SetDataAndCheckLocations)
16209  {
16210  if(ForwardEntryPtr->LocationName == "")
16211  {
16212  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
16213  ". One or other service does not have a location set");
16214  TrainDataVector.clear();
16215  Utilities->CallLogPop(1066);
16216  return(false);
16217  }
16218  if(ReverseEntryPtr->LocationName == "")
16219  {
16220  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
16221  ". One or other service does not have a location set");
16222  TrainDataVector.clear();
16223  Utilities->CallLogPop(1067);
16224  return(false);
16225  }
16226  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
16227  {
16228  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
16229  " is at a different location to the referencing train " + MainHeadCode);
16230  TrainDataVector.clear();
16231  Utilities->CallLogPop(1068);
16232  return(false);
16233  }
16234  }
16235  if(ForwardEntryPtr->Command == "F-nshs")
16236  // i.e. the non repeating link into the shuttle service
16237  {
16238  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
16239  {
16240  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
16241  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
16242  TrainDataVector.clear();
16243  Utilities->CallLogPop(1069);
16244  return(false);
16245  }
16246  }
16247  if(ForwardEntryPtr->Command == "Fns-sh")
16248  // i.e. the non repeating link out from the shuttle service
16249  {
16250  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
16251  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
16252  {
16253  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
16254  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
16255  TrainDataVector.clear();
16256  Utilities->CallLogPop(1070);
16257  return(false);
16258  }
16259  }
16260  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
16261  // i.e. a non repeating link to or from the shuttle service
16262  {
16263  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
16264  {
16265  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
16266  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
16267  TrainDataVector.clear();
16268  Utilities->CallLogPop(1071);
16269  return(false);
16270  }
16271  }
16272 /* it's allowed to have a different description
16273  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
16274  {
16275  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
16276  {
16277  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
16278  TrainDataVector.clear();
16279  Utilities->CallLogPop(1072);
16280  return false;
16281  }
16282  }
16283 */
16284  if(ForwardEntryPtr->Command == "Sns-sh")
16285  {
16286  if(ReverseEntryPtr->Command != "F-nshs")
16287  {
16288  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
16289  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
16290  TrainDataVector.clear();
16291  Utilities->CallLogPop(1073);
16292  return(false);
16293  }
16294  }
16295  if(ForwardEntryPtr->Command == "F-nshs")
16296  {
16297  if(ReverseEntryPtr->Command != "Sns-sh")
16298  {
16299  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
16300  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
16301  TrainDataVector.clear();
16302  Utilities->CallLogPop(1074);
16303  return(false);
16304  }
16305  else
16306  {
16307  if(SetDataAndCheckLocations)
16308  {
16309  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
16310  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
16311  if(OtherTrainDataPtr->Description == "")
16312  {
16313  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
16314  }
16315  }
16316  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
16317  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
16318  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
16319  }
16320  }
16321  if(ForwardEntryPtr->Command == "Sns-fsh")
16322  {
16323  if(ReverseEntryPtr->Command != "Fns-sh")
16324  {
16325  SecondPassMessage(GiveMessages,
16326  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
16327  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
16328  TrainDataVector.clear();
16329  Utilities->CallLogPop(1075);
16330  return(false);
16331  }
16332  }
16333  if(ForwardEntryPtr->Command == "Fns-sh")
16334  {
16335  if(ReverseEntryPtr->Command != "Sns-fsh")
16336  {
16337  SecondPassMessage(GiveMessages,
16338  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
16339  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
16340  TrainDataVector.clear();
16341  Utilities->CallLogPop(1076);
16342  return(false);
16343  }
16344  else
16345  {
16346  if(SetDataAndCheckLocations)
16347  {
16348  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
16349  // links to the non-repeating non-shuttle linked service
16350  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
16351  // needed for creating formatted timetable
16352  if(OtherTrainDataPtr->Description == "")
16353  {
16354  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
16355  }
16356  }
16357  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
16358  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
16359  }
16360  }
16361  Utilities->CallLogPop(1077);
16362  return(true);
16363 }
16364 
16365 // ---------------------------------------------------------------------------
16366 
16367 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
16368 // Forward train is the finish shuttle entry 'Fns-sh'.
16369 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
16370 {
16371  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
16372  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
16373  int ForwardSecs = int(double(ForwardEventTime) * 86400);
16374  int ReverseSecs = int(double(ReverseEventTime) * 86400);
16375  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
16376 
16377  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
16378  {
16379  Utilities->CallLogPop(1369);
16380  return(false);
16381  }
16382  else
16383  {
16384  Utilities->CallLogPop(1370);
16385  return(true);
16386  }
16387 }
16388 
16389 // ---------------------------------------------------------------------------
16390 
16391 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
16392 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
16393 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
16394 // don't ever need to and as designed would skip repeats.
16395 
16396 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
16397 {
16398  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
16399  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
16400  {
16401  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
16402  }
16403  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
16404  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
16405  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
16406 
16407  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
16408  {
16409  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
16410  TrainDataVector.clear();
16411  Utilities->CallLogPop(1091);
16412  return(false);
16413  }
16414  while(LastActionCommand == "Fns")
16415  {
16416  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
16417  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
16418  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
16419  {
16420  SecondPassMessage(GiveMessages,
16421  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
16422  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
16423  TrainDataVector.clear();
16424  Utilities->CallLogPop(1092);
16425  return(false);
16426  }
16427  }
16428  // exit the 'while' with LastActionCommand FSH-XX
16429  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
16430  {
16431  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
16432  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
16433  ". The linking of two or more shuttles is not permitted.");
16434  TrainDataVector.clear();
16435  Utilities->CallLogPop(1093);
16436  return(false);
16437  }
16438  Utilities->CallLogPop(1094);
16439  return(true);
16440 }
16441 
16442 // ---------------------------------------------------------------------------
16443 
16444 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
16445 {
16446  if(!GiveMessages)
16447  {
16448  return;
16449  }
16450  // if(ServiceReference == "") ShowMessage(Message);
16451  if(!CheckHeadCodeValidity(12, false, ServiceReference))
16452  {
16453  ShowMessage(Message);
16454  }
16455  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
16456  // false means don't give messages within the function
16457  else
16458  {
16459  ShowMessage("Service " + ServiceReference + ": " + Message);
16460  }
16461 }
16462 
16463 // ---------------------------------------------------------------------------
16464 
16465 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
16466 {
16467  if(!GiveMessages)
16468  {
16469  return;
16470  }
16471  ShowMessage(Message);
16472 }
16473 
16474 // ---------------------------------------------------------------------------
16475 
16476 AnsiString TTrainController::MinsToAnsiTime(int Input) //added at v2.15.0
16477 {
16478  TrainController->LogEvent("MinsToAnsiTime");
16479  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MinsToAnsiTime," + Input);
16480  int Mins = Input, Hrs = 0;
16481  while(Mins > 59)
16482  {
16483  Mins -= 60;
16484  Hrs++;
16485  }
16486  AnsiString AnsiMins = AnsiString(Mins);
16487  if(AnsiMins.Length() == 1)
16488  {
16489  AnsiMins = "0" + AnsiMins;
16490  }
16491  AnsiString AnsiHrs = AnsiString(Hrs);
16492  if(AnsiHrs.Length() == 1)
16493  {
16494  AnsiHrs = "0" + AnsiHrs;
16495  }
16496  Utilities->CallLogPop(2577);
16497  return(AnsiHrs + ':' + AnsiMins);
16498 }
16499 
16500 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
16501 // ---------------------------------------------------------------------------
16502 
16503 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
16504 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
16505 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
16506 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
16507 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
16508 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
16509 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
16510 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
16511 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
16512 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
16513 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
16514 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
16515 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
16516 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
16517 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
16518 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
16519 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
16520 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
16521 // FailMissedDSC: 06:00:10: ERROR: 2F43 failed to change its description at Essex Road
16522 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
16523 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
16524 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
16525 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
16526 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
16527 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
16528 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
16529 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
16530 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
16531 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
16532 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
16533 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
16534 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5 //added at v2.9.1
16535 {
16536  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
16537  AnsiString(ActionEventType) + "," + LocationID);
16538  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
16539 
16540  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
16541  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode; //added at v2.9.1 to give more info to user
16542 
16543  Prefix = " ERROR: ";
16544  if(ActionEventType == FailTrainEntry)
16545  {
16546  Prefix = " HELD: ";
16547  ErrorLog = " can't enter railway, train obstructing entry position ";
16548  WarningStr = " can't enter railway, train obstructing entry position ";
16549  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
16550  }
16551  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
16552  {
16553  Prefix = " HELD: ";
16554  ErrorLog = " can't enter railway, route set against it at entry position ";
16555  WarningStr = " can't enter railway, route set against it at entry position ";
16556  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
16557  }
16558  else if(ActionEventType == FailCreateTrain)
16559  {
16560  Prefix = " HELD: ";
16561  ErrorLog = " can't be created, train obstructing ";
16562  WarningStr = " can't be created, train obstructing ";
16563  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
16564  }
16565  else if(ActionEventType == FailCreateLockedRoute)
16566  {
16567  Prefix = " HELD: ";
16568  ErrorLog = " can't be created on a locked route at ";
16569  WarningStr = " can't be created on a locked route at ";
16570  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
16571  }
16572  else if(ActionEventType == FailEnterLockedRoute)
16573  {
16574  Prefix = " HELD: ";
16575  ErrorLog = " can't enter on a locked route at ";
16576  WarningStr = " can't enter on a locked route at ";
16577  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
16578  }
16579  else if(ActionEventType == FailCreatePoints)
16580  {
16581  Prefix = " HELD: ";
16582  ErrorLog = " can't be created, diverging points at ";
16583  WarningStr = " can't be created, diverging points at ";
16584  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
16585  }
16586  else if(ActionEventType == FailUnexpectedExitRailway)
16587  {
16588  ErrorLog = " left railway unexpectedly at ";
16589  UnexpectedExits++;
16590  }
16591  else if(ActionEventType == FailIncorrectExit)
16592  {
16593  ErrorLog = " left railway at an incorrect exit at ";
16594  IncorrectExits++;
16595  }
16596  else if(ActionEventType == FailLocTooShort)
16597  {
16598  ErrorLog = " failed to split - location too short at ";
16599  WarningStr = " failed to split, location too short at ";
16600  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
16601  }
16602  else if(ActionEventType == FailSplitDueToOtherTrain)
16603  {
16604  Prefix = " HELD: ";
16605  ErrorLog = " unable to split - other train obstructing at ";
16606  WarningStr = " unable to split - other train obstructing at ";
16607  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
16608  }
16609  else if(ActionEventType == FailUnexpectedBuffers)
16610  {
16611  ErrorLog = " stopped at buffers unexpectedly at position ";
16612  }
16613  else if(ActionEventType == FailMissedArrival)
16614  {
16615  ErrorLog = " failed to stop at ";
16616  MissedStops++;
16617  }
16618  else if(ActionEventType == FailMissedSplit)
16619  {
16620  ErrorLog = " failed to split at ";
16622  }
16623  else if(ActionEventType == FailMissedJBO)
16624  {
16625  ErrorLog = " failed to be joined by other train at ";
16627  }
16628  else if(ActionEventType == FailMissedDSC) //new at v2.15.0
16629  {
16630  ErrorLog = " failed to change its description at ";
16631 // OtherMissedEvents++; shouldn't count
16632  }
16633  else if(ActionEventType == FailMissedJoinOther)
16634  {
16635  ErrorLog = " failed to join other train at ";
16637  }
16638  else if(ActionEventType == FailMissedTerminate)
16639  {
16640  ErrorLog = " failed to terminate at ";
16642  }
16643  else if(ActionEventType == FailMissedNewService)
16644  {
16645  ErrorLog = " failed to form new service at ";
16647  }
16648  else if(ActionEventType == FailMissedExitRailway)
16649  {
16650  ErrorLog = " failed to exit railway ";
16652  }
16653  else if(ActionEventType == FailMissedChangeDirection)
16654  {
16655  ErrorLog = " failed to change direction at ";
16656 // OtherMissedEvents++; //dropped at v2.12.0 as cdt shouldn't count
16657  }
16658  else if(ActionEventType == FailMissedPass)
16659  {
16660  ErrorLog = " failed to pass ";
16661 // OtherMissedEvents++; //dropped at v2.12.0 as missed pass shouldn't count
16662  }
16663  else if(ActionEventType == FailBuffersPreventingStart)
16664  {
16665  ErrorLog = " facing buffers and unable to start at ";
16666  }
16667  else if(ActionEventType == FailDerailed)
16668  {
16669  ErrorLog = " DERAILED at position ";
16670  Prefix = " DERAILMENT: ";
16671  Derailments++;
16672  }
16673  else if(ActionEventType == FailBufferCrash)
16674  {
16675  ErrorLog = " CRASHED INTO BUFFERS at ";
16676  Prefix = " CRASH: ";
16677  CrashedTrains++;
16678  }
16679  else if(ActionEventType == FailLevelCrossingCrash)
16680  {
16681  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
16682  Prefix = " CRASH: ";
16683  CrashedTrains++;
16684  }
16685  else if(ActionEventType == FailCrashed)
16686  {
16687  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
16688  Prefix = " CRASH: ";
16689  CrashedTrains++;
16690  CrashedTrains++;
16691  }
16692  else if(ActionEventType == FailSPAD)
16693  {
16694  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
16695  Prefix = " SPAD: ";
16696  SPADEvents++;
16697  }
16698  else if(ActionEventType == FailLockedRoute)
16699  {
16700  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
16701  Prefix = " SPAD RISK: ";
16702  SPADRisks++;
16703  }
16704  else if(ActionEventType == RouteForceCancelled)
16705  {
16706  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
16707  }
16708  else if(ActionEventType == WaitingForJBO)
16709  {
16710  Prefix = " WARNING: ";
16711  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
16712  WarningStr = " waiting to join " + OtherHeadCode + " at ";
16713  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
16714  }
16715  else if(ActionEventType == WaitingForFJO)
16716  {
16717  Prefix = " WARNING: ";
16718  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
16719  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
16720  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
16721  }
16722 
16723  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
16724  PerfLogForm->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
16725  Utilities->CallLogPop(1371);
16726 }
16727 
16728 // ---------------------------------------------------------------------------
16729 
16731 {
16732 /* //for testing purposes
16733  TrainDataEntry
16734  AnsiString HeadCode, Description;//null on creation
16735  int StartSpeed, MaxRunningSpeed;//both kph
16736  int RepeatNumber;
16737  TActionVector ActionVector;
16738  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
16739  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
16740 
16741  ActionVectorEntry
16742  TTimetableEntryType FormatType;
16743  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
16744  AnsiString LocationName, Command, OtherHeadCode;//null on creation
16745  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
16746  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
16747  int RepeatNumber;
16748 
16749  TrainOperatingData
16750  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
16751  int TrainID;
16752  TRunningEntry RunningEntry;
16753  TDateTime StartTime;
16754  AnsiString HeadCode;
16755 */
16756  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
16757  std::ofstream OutFile("TrainData.csv");
16758 
16759  if(OutFile == 0)
16760  {
16761  ShowMessage("Output file TrainData.csv failed to open");
16762  Utilities->CallLogPop(1372);
16763  return;
16764  }
16765  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16766  {
16767  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
16768  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
16769 
16770  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
16771  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
16772 
16773  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
16774  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
16775  "RepeatNumber" << '\n' << '\n';
16776  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
16777  {
16778  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
16779  AnsiString TimetableEntryTypeStr;
16780  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
16781  switch(AVEntry.FormatType)
16782  {
16783  case 0:
16784  {
16785  TimetableEntryTypeStr = "NoFormat";
16786  break;
16787  }
16788 
16789  case 1:
16790  {
16791  TimetableEntryTypeStr = "TimeLoc";
16792  break;
16793  }
16794 
16795  case 2:
16796  {
16797  TimetableEntryTypeStr = "TimeTimeLoc";
16798  break;
16799  }
16800 
16801  case 3:
16802  {
16803  TimetableEntryTypeStr = "TimeCmd";
16804  break;
16805  }
16806 
16807  case 4:
16808  {
16809  TimetableEntryTypeStr = "StartNew";
16810  break;
16811  }
16812 
16813  case 5:
16814  {
16815  TimetableEntryTypeStr = "TimeCmdHeadCode";
16816  break;
16817  }
16818 
16819  case 6:
16820  {
16821  TimetableEntryTypeStr = "FinRemHere";
16822  break;
16823  }
16824 
16825  case 7:
16826  {
16827  TimetableEntryTypeStr = "FNSShuttle";
16828  break;
16829  }
16830 
16831  case 8:
16832  {
16833  TimetableEntryTypeStr = "SNTShuttle";
16834  break;
16835  }
16836 
16837  case 9:
16838  {
16839  TimetableEntryTypeStr = "SNSShuttle";
16840  break;
16841  }
16842 
16843  case 10:
16844  {
16845  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
16846  break;
16847  }
16848 
16849  case 11:
16850  {
16851  TimetableEntryTypeStr = "FSHNewService";
16852  break;
16853  }
16854 
16855  case 12:
16856  {
16857  TimetableEntryTypeStr = "Repeat";
16858  break;
16859  }
16860 
16861  default:
16862  {
16863  TimetableEntryTypeStr = "Default";
16864  break;
16865  }
16866  }
16867  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
16868  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
16869  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
16870  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
16871  AVEntry.NumberOfRepeats << '\n';
16872  }
16873  OutFile << '\n';
16874  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
16875  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
16876  {
16877  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
16878  AnsiString RunningEntryStr;
16879  // NotStarted, Running, Exited
16880  switch(TOD.RunningEntry)
16881  {
16882  case 0:
16883  {
16884  RunningEntryStr = "NotStarted";
16885  break;
16886  }
16887 
16888  case 1:
16889  {
16890  RunningEntryStr = "Running";
16891  break;
16892  }
16893 
16894  case 2:
16895  {
16896  RunningEntryStr = "Exited";
16897  break;
16898  }
16899  }
16900  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
16901  }
16902  OutFile << '\n';
16903  }
16904  OutFile.close();
16905  Utilities->CallLogPop(1373);
16906 }
16907 
16908 // ---------------------------------------------------------------------------
16909 
16910 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
16911 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
16912 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed (in ClockTimer2 when StopTTClockFlag is false)
16913 {
16914  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
16915  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
16917  ShowMessage(Message);
16918  BaseTime = TDateTime::CurrentDateTime();
16919  StopTTClockFlag = false;
16920  Utilities->CallLogPop(1374);
16921 }
16922 
16923 // ---------------------------------------------------------------------------
16924 
16925 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
16926 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
16927 // from the start of the relevant vectors. Can't save the pointer values
16928 // as these will be different each time the vectors are created
16929 {
16930  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
16931  Utilities->SaveFileInt(SessionFile, TrainVector.size());
16932  for(unsigned int x = 0; x < TrainVector.size(); x++)
16933  {
16934  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
16935  }
16936  Utilities->CallLogPop(1375);
16937 }
16938 
16939 // ---------------------------------------------------------------------------
16940 
16941 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
16942 {
16943  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
16944  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
16945  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
16946  // by zero error in calculating AValue, use 1
16947  for(int x = 0; x < NumberOfTrains; x++)
16948  {
16949  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
16950  // by zero error in calculating AValue, use 1
16951  NewTrain->LoadOneSessionTrain(0, SessionFile);
16952  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
16953  // added at v2.4.0. have to include as that value not stored in session file
16954  {
16955  NewTrain->StoppedWithoutPower = true;
16956  }
16957  TrainVector.push_back(*NewTrain);
16958  LastTrainLoaded = x;
16959  }
16960  delete NewTrain;
16961  Utilities->CallLogPop(1376);
16962 }
16963 
16964 // ---------------------------------------------------------------------------
16965 
16966 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
16967 {
16968  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
16969  int NumberOfTrains;
16970 
16971  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
16972  {
16973  Utilities->CallLogPop(1377);
16974  return(false);
16975  }
16976  for(int x = 0; x < NumberOfTrains; x++)
16977  {
16978  if(!(TTrain::CheckOneSessionTrain(InFile)))
16979  {
16980  Utilities->CallLogPop(1378);
16981  return(false);
16982  }
16983  }
16984  Utilities->CallLogPop(1379);
16985  return(true);
16986 }
16987 
16988 // ---------------------------------------------------------------------------
16989 
16990 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
16991 {
16992  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
16993  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
16994  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
16995  {
16996  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
16997  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RearTrackVectorPosition);
16998  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
16999  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
17000  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
17001  }
17002  Utilities->CallLogPop(1380);
17003 }
17004 
17005 // ---------------------------------------------------------------------------
17006 
17007 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
17008 {
17009  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
17010  TAllRoutes::TLockedRouteClass LockedRouteObject;
17011  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
17012 
17013  for(int x = 0; x < LockedRouteVectorSize; x++)
17014  {
17015  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
17016  LockedRouteObject.RearTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
17017  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
17018  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
17019  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
17020  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
17021  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
17022  }
17023  Utilities->CallLogPop(1381);
17024 }
17025 
17026 // ---------------------------------------------------------------------------
17027 
17028 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
17029 {
17030  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
17031  int LockedRouteVectorSize;
17032 
17033  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
17034  {
17035  Utilities->CallLogPop(1382);
17036  return(false);
17037  }
17038  for(int x = 0; x < LockedRouteVectorSize; x++)
17039  {
17040  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
17041  {
17042  Utilities->CallLogPop(1383);
17043  return(false);
17044  }
17045  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
17046  {
17047  Utilities->CallLogPop(1384);
17048  return(false);
17049  }
17050  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
17051  {
17052  Utilities->CallLogPop(1385);
17053  return(false);
17054  }
17055  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
17056  {
17057  Utilities->CallLogPop(1386);
17058  return(false);
17059  }
17060  if(!Utilities->CheckFileDouble(SessionFile))
17061  {
17062  Utilities->CallLogPop(1387);
17063  return(false);
17064  }
17065  }
17066  Utilities->CallLogPop(1388);
17067  return(true);
17068 }
17069 
17070 // ---------------------------------------------------------------------------
17071 
17072 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
17073 {
17074  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
17075  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
17076  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
17077  {
17078  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
17079  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
17080  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
17081  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
17082  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
17083  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
17084  }
17085  Utilities->CallLogPop(1389);
17086 }
17087 
17088 // ---------------------------------------------------------------------------
17089 
17090 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
17091 {
17092  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
17093  TContinuationAutoSigEntry ContinuationAutoSigObject;
17094  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
17095 
17096  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
17097  {
17098  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
17099  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
17100  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
17101  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
17102  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
17103  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
17104  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
17105  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
17106  }
17107  Utilities->CallLogPop(1390);
17108 }
17109 
17110 // ---------------------------------------------------------------------------
17111 
17112 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
17113 {
17114  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
17115  int ContinuationAutoSigVectorSize;
17116 
17117  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
17118  {
17119  Utilities->CallLogPop(1391);
17120  return(false);
17121  }
17122  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
17123  {
17124  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
17125  {
17126  Utilities->CallLogPop(1392);
17127  return(false);
17128  }
17129  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
17130  {
17131  Utilities->CallLogPop(1393);
17132  return(false);
17133  }
17134  if(!Utilities->CheckFileDouble(SessionFile))
17135  {
17136  Utilities->CallLogPop(1405);
17137  return(false);
17138  }
17139  if(!Utilities->CheckFileDouble(SessionFile))
17140  {
17141  Utilities->CallLogPop(1406);
17142  return(false);
17143  }
17144  if(!Utilities->CheckFileDouble(SessionFile))
17145  {
17146  Utilities->CallLogPop(1407);
17147  return(false);
17148  }
17149  if(!Utilities->CheckFileDouble(SessionFile))
17150  {
17151  Utilities->CallLogPop(1394);
17152  return(false);
17153  }
17154  }
17155  Utilities->CallLogPop(1395);
17156  return(true);
17157 }
17158 
17159 // ---------------------------------------------------------------------------
17160 
17161 /*
17162  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
17163  {
17164  public:
17165  AnsiString Description; ///< service description
17166  AnsiString HeadCode; ///< service headcode
17167  int RepeatNumber; ///< service RepeatNumber
17168  int IncrementalMinutes; ///< Repeat separation in minutes
17169  int IncrementalDigits; ///< Repeat headcode separation
17170  int VectorPosition; ///< TrackVectorPosition for the continuation element
17171  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
17172  };
17173 
17174 
17175  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
17176  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
17177 */
17178 
17180 // build this into timetable load so session loading can use it too
17181 // being a multimap it automatically sorts in ascending EventTime order
17182 {
17183  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
17185  // need to clear as this called twice when load a session
17186  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
17187  {
17188  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
17189  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
17190  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
17191 
17192  if(AVFirstEntry.Command == "Snt")
17193  // new train (no need to include Snt-sh since they can't start at a continuation)
17194  {
17197  {
17199  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
17200  // retains this value for all repeats
17201  CTEEntry.RepeatNumber = 0; // for first entry
17202  CTEEntry.TrainDataEntryPtr = &TDEntry;
17203  // retains this value for all repeats
17204  CTEEntry.HeadCode = TDEntry.HeadCode;
17205  CTEEntry.Description = TDEntry.Description;
17206  CTEEntry.IncrementalMinutes = 0;
17207  CTEEntry.IncrementalDigits = 0;
17208  if(AVLastEntry.FormatType == Repeat)
17209  {
17210  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
17211  // retains this value or 0 for all repeats
17212  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
17213  // retains this value or 0 for all repeats
17214  }
17215  CTEMMP.first = AVFirstEntry.EventTime;
17216  CTEMMP.second = CTEEntry;
17217  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
17218  // base entry
17219  if(TDEntry.NumberOfTrains > 1)
17220  {
17221  if(AVLastEntry.FormatType != Repeat)
17222  {
17223  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
17224  }
17225  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
17226  {
17227  CTEEntry.RepeatNumber = y;
17228  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
17229  // CTEEntry.VectorPosition stays same
17230  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
17231  CTEMMP.second = CTEEntry;
17232  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
17233  }
17234  }
17235  }
17236  }
17237  }
17238  Utilities->CallLogPop(1396);
17239 }
17240 
17241 // ---------------------------------------------------------------------------
17242 
17244 {
17245  // called when WarningFlashCount == 0 or when press zoomout button
17246  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
17247  if(!Display->ZoomOutFlag)
17248  {
17249  Utilities->CallLogPop(1156);
17250  return;
17251  }
17252  for(unsigned int x = 0; x < TrainVector.size(); x++)
17253  {
17254  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
17255  // if OldPlotElement[x] == -1 then ignore (not plotted)
17257  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
17258  }
17259  Display->Update();
17260  // need to keep this since Update() not called for PlotSmallOutput as too slow
17261  Utilities->CallLogPop(742);
17262 }
17263 
17264 // ---------------------------------------------------------------------------
17265 
17266 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
17267 {
17268  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
17269  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
17270  {
17271  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
17272  }
17273  Utilities->CallLogPop(740);
17274  return(TrainVector.at(VecPos));
17275 }
17276 
17277 // ---------------------------------------------------------------------------
17278 
17279 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
17280 {
17281  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
17282  AnsiString RetStr = "", PartStr = "";
17283 
17284 
17285 /*
17286  Have description & mass etc for train at top - header, then array of actions
17287 
17288  class TActionVectorEntry
17289  {
17290  public:
17291  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
17293  bool SignallerControl;
17295  bool Warning;
17297  int NumberOfRepeats;
17299  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
17301  TDateTime EventTime, ArrivalTime, DepartureTime;
17303  TNumList ExitList;
17305  TTimetableFormatType FormatType;
17307  TTimetableLocationType LocationType;
17309  TTimetableSequenceType SequenceType;
17311  TTimetableShuttleLinkType ShuttleLinkType;
17313  TTrainDataEntry *LinkedTrainEntryPtr;
17315  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
17317 
17318  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
17319 
17320  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
17321 
17322  class TTrainOperatingData
17323  {
17324  public:
17325  int TrainID;
17326  TActionEventType EventReported;
17327  TRunningEntry RunningEntry;
17328 
17329  //inline function
17330  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
17331  };
17332 
17333  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
17334 
17335  class TTrainDataEntry
17336  {
17337  public:
17338  AnsiString HeadCode, ServiceReference, Description;
17340  double MaxBrakeRate;
17342  double MaxRunningSpeed;
17344  double PowerAtRail;
17346  int Mass;
17348  int NumberOfTrains;
17350  int SignallerSpeed;
17352  int StartSpeed;
17354  TActionVector ActionVector;
17356  TTrainOperatingDataVector TrainOperatingDataVector;
17358 
17359  //inline function
17360  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
17361  };
17362 
17363  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
17364 
17365  //formatted timetable types
17366  class TOneTrainFormattedEntry
17367  {
17368  AnsiString Action;//includes location if relevanr
17369  AnsiString Time;
17370  };
17371 
17372  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
17373 
17374  class TOneCompleteFormattedTrain//headcode + list of actions
17375  {
17376  public:
17377  AnsiString HeadCode;
17378  TOneFormattedTrainVector OneFormattedTrainVector;
17379  };
17380 
17381  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
17382 
17383  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
17384  {
17385  public:
17386  AnsiString Header;//description, mass, power, brake rate etc
17387  int NumberOfTrains;// number of repeats + 1
17388  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
17389  };
17390 
17391 
17392  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
17393  //end of formatted timetable types
17394 
17395 */
17396 
17397  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17398 
17399  // format "16/06/2009 20:55:17"
17400  // avoid characters in filename:= / \ : * ? " < > |
17401  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
17402 
17403  AnsiString ShortTTName = "";
17404 
17405  for(int x = TTFileName.Length(); x > 0; x--)
17406  {
17407  if(TTFileName[x] == '\\')
17408  {
17409  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
17410  break;
17411  }
17412  }
17413 
17414  ShowMessage("Creates two timetables named " + ShortTTName +
17415  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
17416 
17417  Screen->Cursor = TCursor(-11); // Hourglass
17418 
17419  AnsiString FormatNoDPStr = "#######0";
17420  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
17421 
17423  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
17424  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
17425 
17426  // all timetable in formatted form
17427  //create the AllTTTrains vector
17428  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
17429  {
17430  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
17431  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
17432  if(TrainDataEntry.Mass > 0)
17433  {
17434  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
17435  }
17436  if(TrainDataEntry.PowerAtRail > 0)
17437  {
17438  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
17439  }
17440  if(TrainDataEntry.MaxBrakeRate > 0)
17441  {
17442  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
17443  }
17444  if(TrainDataEntry.MaxRunningSpeed > 0)
17445  {
17446  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
17447  }
17448  FirstHeadCode = TrainDataEntry.HeadCode;
17449  int IncDigits = 0, IncMinutes = 0;
17450  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
17451  if(!ActionVector.empty())
17452  {
17453  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
17454  {
17455  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
17456  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
17457  }
17458  }
17459  TTrainFormattedInformation OneTTLine;
17460  // contains all information for a single TT entry (including repeats)
17461  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
17462  {
17463  OneTTLine.Header = "";
17464  if((TrainDataEntry.Description != "") && (MassStr != ""))
17465  {
17466  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
17467  }
17468  else if(TrainDataEntry.Description != "")
17469  {
17470  OneTTLine.Header = TrainDataEntry.Description;
17471  }
17472  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
17473  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
17474  for(unsigned int z = 0; z < ActionVector.size(); z++)
17475  {
17476  TOneTrainFormattedEntry OneTTEntry;
17477  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
17478  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
17479  AnsiString PartStr = "", TimeStr = "";
17480 /*
17481  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
17482  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
17483  ExitRailway};
17484  enum TTimetableSequenceType {NoSequence, StartSequence, FinishSequence, IntermediateSequence, SequTypeForRepeatEntry};
17485  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
17486  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
17487 */
17488  if(ActionVectorEntry.SequenceType == StartSequence)
17489  {
17490  if(ActionVectorEntry.FormatType == StartNew)
17491  {
17492  if(ActionVectorEntry.LocationName != "")
17493  {
17494  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
17495  {
17496  PartStr = "Enters at " + ActionVectorEntry.LocationName;
17497  }
17498  else
17499  {
17500  PartStr = "Created at " + ActionVectorEntry.LocationName;
17501  }
17502  }
17503  else // may be a named continuation or other element, and if so report that
17504  {
17505  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
17506  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
17507  {
17508  if(LocName != "")
17509  {
17510  PartStr = "Enters at " + LocName;
17511  }
17512  else // use rear position if it's a continuation
17513  {
17514  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
17515  }
17516  }
17517  else // not a continuation
17518  {
17519  if(LocName != "")
17520  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
17521  // but include anyway
17522  {
17523  PartStr = "Created at " + LocName;
17524  }
17525  else // use rear position again
17526  {
17527  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
17528  }
17529  }
17530  }
17531  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
17532  }
17533  else if(ActionVectorEntry.FormatType == SNTShuttle)
17534  {
17535  if(y == 0) // first train
17536  {
17537  PartStr = "Enters at " + ActionVectorEntry.LocationName;
17538  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
17539  }
17540  else
17541  {
17542  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
17543  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
17544  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
17545  } // y-1 for headcode above since it is the last repeat value that the train is from
17546 
17547  }
17548  else if(ActionVectorEntry.Command == "Sfs")
17549  {
17550  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
17551  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17552  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
17553  }
17554  else if(ActionVectorEntry.Command == "Sns")
17555  {
17556  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
17557  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17558  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
17559  }
17560  else if(ActionVectorEntry.FormatType == SNSShuttle)
17561  {
17562  if(y == 0) // first entry from shuttle
17563  {
17564  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
17565  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
17566  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
17567  }
17568  else
17569  {
17570  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
17571  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
17572  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
17573  } // y-1 for headcode above since it is the last repeat value that the train is from
17574 
17575  }
17576  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
17577  {
17578  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
17579  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
17580  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
17581  AnsiString FirstHeadCode = TDE->HeadCode;
17582  int LastRepeatNumber = TDE->NumberOfTrains - 1;
17583  // a shuttle has to have at least 1 repeat
17584  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
17585  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
17586  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
17587  }
17588  }
17589  else if(ActionVectorEntry.SequenceType == IntermediateSequence)
17590  {
17591  if(ActionVectorEntry.FormatType == TimeTimeLoc)
17592  {
17593  // here need 2 entries if times different so push the first right away & the second later
17594  // if times same just give the arrival entry
17595  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
17596  {
17597  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
17598  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
17599  OneTTEntry.Action = PartStr;
17600  OneTTEntry.Time = TimeStr;
17601  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
17602  PartStr = "Departs from " + ActionVectorEntry.LocationName;
17603  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
17604  }
17605  else
17606  {
17607  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
17608  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
17609  }
17610  }
17611  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
17612  {
17613  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
17614  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
17615  }
17616  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
17617  {
17618  PartStr = "Departs from " + ActionVectorEntry.LocationName;
17619  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
17620  }
17621  else if(ActionVectorEntry.FormatType == PassTime)
17622  {
17623  PartStr = "Passes " + ActionVectorEntry.LocationName;
17624  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
17625  }
17626  else if(ActionVectorEntry.Command == "jbo")
17627  {
17628  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
17629  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17630  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
17631  }
17632  else if(ActionVectorEntry.Command == "fsp")
17633  {
17634  if(ActionVectorEntry.SplitDistribution != "") //new at v2.15.0
17635  {
17636  PartStr = "Splits from front [mass%-power% = " + ActionVectorEntry.SplitDistribution + "] at " + ActionVectorEntry.LocationName + " to form";
17637  }
17638  else
17639  {
17640  PartStr = "Splits from front [mass%-power% = 50-50] at " + ActionVectorEntry.LocationName + " to form";
17641  }
17642  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17643  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
17644  }
17645  else if(ActionVectorEntry.Command == "rsp")
17646  {
17647  if(ActionVectorEntry.SplitDistribution != "") //new at v2.15.0
17648  {
17649  PartStr = "Splits from front [mass%-power% = " + ActionVectorEntry.SplitDistribution + "] at " + ActionVectorEntry.LocationName + " to form";
17650  }
17651  else
17652  {
17653  PartStr = "Splits from front [mass%-power% = 50-50] at " + ActionVectorEntry.LocationName + " to form";
17654  }
17655  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17656  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
17657  }
17658  else if(ActionVectorEntry.Command == "cdt")
17659  {
17660  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
17661  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
17662  }
17663  else if(ActionVectorEntry.Command == "dsc")
17664  {
17665  PartStr = "Changes description at " + ActionVectorEntry.LocationName;
17666  TimeStr = Utilities->Format96HHMM(GetRepeatTime(76, ActionVectorEntry.EventTime, y, IncMinutes));
17667  }
17668  }
17669  else if(ActionVectorEntry.SequenceType == FinishSequence)
17670  {
17671  if(ActionVectorEntry.Command == "Fns")
17672  {
17673  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17674  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17675  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
17676  }
17677  else if(ActionVectorEntry.Command == "F-nshs")
17678  {
17679  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17680  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
17681  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
17682  }
17683  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
17684  {
17685  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
17686  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
17687  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
17688  // y+1 because it's the NEXT service repeat number that is relevant
17689  }
17690  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
17691  {
17692  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17693  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
17694  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
17695  }
17696  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
17697  {
17698  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17699  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
17700  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
17701  // y+1 because it's the NEXT service repeat number that is relevant
17702  }
17703  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
17704  {
17705  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
17706  // only used in chronological tt
17707  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
17708  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
17709  }
17710  else if(ActionVectorEntry.Command == "Frh")
17711  {
17712  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
17713  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
17714  if(z > 0)
17715  // should be for finish entry but include check for safety
17716  {
17717  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
17718  {
17719  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
17720  }
17721  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
17722  {
17723  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
17724  }
17725  else
17726  {
17727  TimeStr = " "; // shouldn't ever get here
17728  }
17729  }
17730  }
17731  else if(ActionVectorEntry.Command == "Fer")
17732  {
17733  AnsiString AllowedExits;
17734  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
17735  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
17736  }
17737  else if(ActionVectorEntry.Command == "Fjo")
17738  {
17739  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
17740  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17741  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
17742  }
17743  }
17744  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
17745  {
17746  continue; // no entry needed for a repeat
17747  }
17748  OneTTEntry.Action = PartStr;
17749  OneTTEntry.Time = TimeStr;
17750  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
17751  // one per action
17752  }
17753  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
17754  // one per repeat
17755  }
17756  AllTTTrains->push_back(OneTTLine); // one per repeating train
17757  }
17758  // AllTTTrains vector now complete
17759 
17760  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
17761 
17762  if(TTFile == 0)
17763  {
17764  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
17765  delete AllTTTrains;
17766  Utilities->CallLogPop(1567);
17767  return;
17768  }
17769 /* formatted timetable types
17770  class TOneTrainFormattedEntry
17771  {
17772  AnsiString Action;//includes location if relevant
17773  AnsiString Time;
17774  };
17775 
17776  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
17777 
17778  class TOneCompleteFormattedTrain//headcode + list of actions
17779  {
17780  public:
17781  AnsiString HeadCode;
17782  TOneFormattedTrainVector OneFormattedTrainVector;
17783  };
17784 
17785  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
17786 
17787  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
17788  {
17789  public:
17790  AnsiString Header;//description, mass, power, brake rate etc
17791  int NumberOfTrains;// number of repeats + 1
17792  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
17793  };
17794 
17795  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
17796  //end of formatted timetable types
17797 */
17798 
17799  // new layout using multiple rows
17800  TTFile << TableTitle.c_str() << '\n' << '\n';
17801  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
17802  {
17803  TTFile << AllTTTrains->at(x).Header.c_str();
17804  TTFile << '\n';
17805  TTFile << ','; // for the blank line above the action list
17806  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17807  {
17808  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
17809  {
17810  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
17811  }
17812  else
17813  {
17814  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
17815  }
17816  }
17817  TTFile << '\n' << '\n';
17818 
17819  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
17820  {
17821  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
17822  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17823  {
17824  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
17825  {
17826  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
17827  }
17828  else
17829  {
17830  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
17831  }
17832  }
17833  TTFile << '\n';
17834  }
17835  TTFile << '\n' << '\n';
17836  }
17837 
17838  TTFile.close();
17839 
17840  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17841 
17842  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
17843 
17844  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
17845 
17846  if(TTFile2 == 0)
17847  {
17848  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
17849  delete AllTTTrains;
17850  Utilities->CallLogPop(1710);
17851  return;
17852  }
17853  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
17854  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
17855  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
17856 
17857  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
17858  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
17859 
17860  // multimap of AnsiStrings with TimeString as key (to sort automatically)
17861 
17862  TTFile2 << TableTitle.c_str() << '\n' << '\n';
17863  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
17864  {
17865  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17866  {
17867  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
17868  {
17869  bool GiveMessagesFalse = false;
17870  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
17871  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
17872  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
17873  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
17874  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
17875  {
17876  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
17877  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
17878  TimeString = TimeString.SubString(9, 5);
17879  ActionString += " " + OtherHeadCode;
17880  }
17881  if(TimeString.SubString(1, 7) == "End at ")
17882  // for Frh-sh final entry
17883  {
17884  TimeString = TimeString.SubString(8, 5);
17885  }
17886  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
17887  AnsiMultiMapEntry.first = TimeString;
17888  AnsiMultiMapEntry.second = OneLine;
17889  TAMM->insert(AnsiMultiMapEntry);
17890  }
17891  }
17892  }
17893 
17894  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
17895  {
17896  TTFile2 << (AMMIT->second).c_str();
17897  }
17898  delete AllTTTrains;
17899  delete TAMM;
17900  TTFile2.close();
17901  Utilities->CallLogPop(1580);
17902 }
17903 
17904 // ---------------------------------------------------------------------------
17905 
17906 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
17907  bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
17908 {
17909 
17910  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
17911  bool AnalysisError = false;
17912  AnsiString SequenceLog = "SequenceLog\n";
17913 
17914 /* Double crosslink (shuttle) table:
17915 
17916 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
17917  Code ShuttleLink- EntryPtr ShuttleLink-
17918  HeadCode EntryPtr
17919 
17920 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
17921 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
17922 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
17923 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
17924 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
17925 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
17926 
17927 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
17928 */
17929 
17930  try
17931  {
17932  //New section at v2.5.0 for tt conflict analysis
17933  /*
17934  typedef std::list<AnsiString> TServiceCallingLocsList;
17935  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
17936 
17938  struct TLocServiceTimes
17939  {
17940  AnsiString Location;
17941  AnsiString ServiceAndRepeatNum;
17942  AnsiString AtLocTime;
17943  AnsiString ArrTime;
17944  AnsiString DepTime;
17945  AnsiString FrhMarker;
17946  };
17947  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
17948  */
17949 
17950  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
17951  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
17952 
17953 //create TrainDataVectorCopy and populate service refs with /1, /2 etc
17954  TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
17955  TTrainDataVector::iterator TDVIt, TDVCopyIt;
17956  int Suffix = 0;
17957  int IteratorNumber = 0;
17958  AnsiString AnsiSuffix = "";
17959  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
17960  {
17961  IteratorNumber++; //first value in loop is 1
17962  Suffix = 0;
17963  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
17964  {
17965  if(TDVCopyIt->HeadCode == TDVIt->HeadCode)
17966  {
17967  Suffix++; //first value is 1
17968  AnsiSuffix = AnsiString(Suffix);
17969  TDVCopyIt->ServiceReference = TDVIt->HeadCode + "/" + AnsiSuffix; //set both the HeadCode + any forward slashes and numbers, this is because sometimes
17970  TDVCopyIt->HeadCode = TDVIt->HeadCode + "/" + AnsiSuffix; //service refs are used and sometimes H/Cs, so need them to be the same,
17971  } //they are all unique at this point anyway
17972  }
17973  }
17974 //now make all linked pointers in ActionVectorEntries point to links in the vector copy (still point to original vector at this stage)
17975 //and set the linked headcodes to the correct values - all unique at this point
17976  int Increment = 0, SlashPos;
17977  TActionVectorIterator AVEIt;
17978  AnsiString LinkedHeadCode;
17979 
17980  for(TDVCopyIt = TrainDataVectorCopy.begin(); TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
17981  {
17982  for(AVEIt = TDVCopyIt->ActionVector.begin(); AVEIt != TDVCopyIt->ActionVector.end(); AVEIt++)
17983  {
17984  if(AVEIt->LinkedTrainEntryPtr != NULL)
17985  {
17986  Increment = AVEIt->LinkedTrainEntryPtr - &TrainDataVector.at(0);
17987  AVEIt->LinkedTrainEntryPtr = &TrainDataVectorCopy.at(0) + Increment;
17988  //now set AVEIt->OtherHeadCode to the linked headcodes with /1, /2 etc
17989  //but note that these ARE HeadCodes and not service refs, so need to strip off any prefixes from the linked service refs
17990  LinkedHeadCode = (*AVEIt->LinkedTrainEntryPtr).ServiceReference;
17991  //now count from back until reach a '/' character or a non-integer character, if reach non-integer first then no '/' present at end of H/c (but may be one earlier as a prefix)
17992  SlashPos = 0;
17993  for(int x = LinkedHeadCode.Length(); x > 0; x--)
17994  {
17995  if(LinkedHeadCode[x] == '/')
17996  {
17997  SlashPos = LinkedHeadCode.Length() - x + 1;
17998  break;
17999  }
18000  else if((LinkedHeadCode[x] != '0') && (LinkedHeadCode[x] != '1') && (LinkedHeadCode[x] != '2') && (LinkedHeadCode[x] != '3') &&
18001  (LinkedHeadCode[x] != '4') && (LinkedHeadCode[x] != '5') && (LinkedHeadCode[x] != '6') && (LinkedHeadCode[x] != '7') &&
18002  (LinkedHeadCode[x] != '8') && (LinkedHeadCode[x] != '8'))
18003  {
18004  break;
18005  }
18006  }
18007  //now strip off any prefix
18008  AVEIt->OtherHeadCode = LinkedHeadCode.SubString(LinkedHeadCode.Length() - 3 - SlashPos, 4 + SlashPos);
18009  }
18010  else
18011  {
18012  AVEIt->OtherHeadCode = "";
18013  }
18014  if(AVEIt->NonRepeatingShuttleLinkEntryPtr != NULL)
18015  {
18016  Increment = AVEIt->NonRepeatingShuttleLinkEntryPtr - &TrainDataVector.at(0);
18017  AVEIt->NonRepeatingShuttleLinkEntryPtr = &TrainDataVectorCopy.at(0) + Increment;
18018  //now set AVEIt->NonRepeatingShuttleLinkHeadCode to the linked headcodes with /1, /2 etc
18019  //but note that these ARE HeadCodes and not service refs, so need to strip off any prefixes from the linked service refs
18020  LinkedHeadCode = (*AVEIt->NonRepeatingShuttleLinkEntryPtr).ServiceReference;
18021  //now count from back until reach a '/' character or a non-integer character, if reach non-integer first then no '/' present at end of H/c (but may be one earlier as a prefix)
18022  SlashPos = 0;
18023  for(int x = LinkedHeadCode.Length(); x > 0; x--)
18024  {
18025  if(LinkedHeadCode[x] == '/')
18026  {
18027  SlashPos = LinkedHeadCode.Length() - x + 1;
18028  break;
18029  }
18030  else if((LinkedHeadCode[x] != '0') && (LinkedHeadCode[x] != '1') && (LinkedHeadCode[x] != '2') && (LinkedHeadCode[x] != '3') &&
18031  (LinkedHeadCode[x] != '4') && (LinkedHeadCode[x] != '5') && (LinkedHeadCode[x] != '6') && (LinkedHeadCode[x] != '7') &&
18032  (LinkedHeadCode[x] != '8') && (LinkedHeadCode[x] != '8'))
18033  {
18034  break;
18035  }
18036  }
18037  //now strip off any prefix
18038  AVEIt->NonRepeatingShuttleLinkHeadCode = LinkedHeadCode.SubString(LinkedHeadCode.Length() - 3 - SlashPos, 4 + SlashPos);
18039  }
18040  else
18041  {
18042  AVEIt->NonRepeatingShuttleLinkHeadCode = "";
18043  }
18044  }
18045  }
18046  //from here only TrainDataVectorCopy used
18047  SequenceLog += "1\n";
18048  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
18049  TServiceCallingLocsList ServiceCallingLocsList;
18050  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
18051  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
18052  {
18053  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
18054  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
18055  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
18056  ServiceCallingLocsList.clear();
18057  if(ActionVector.empty())
18058  {
18059  continue;
18060  }
18061  if(ActionVector.at(0).SignallerControl)
18062  {
18063  continue;
18064  }
18065  for(unsigned int z = 0; z < ActionVector.size(); z++)
18066  {
18067  TActionVectorEntry AVE = ActionVector.at(z);
18068  if(AVE.FormatType == StartNew)
18069  {
18070  if(AVE.LocationType == AtLocation) //located Snt
18071  {
18072  ServiceCallingLocsList.push_back(AVE.LocationName);
18073  }
18074  else //unlocated Snt (could be entering at continuation)
18075  {
18077  if(TE.ActiveTrackElementName != "")
18078  {
18079  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
18080  }
18081  else
18082  {
18083  int HLoc = TE.HLoc;
18084  int VLoc = TE.VLoc;
18085  AnsiString HString;
18086  AnsiString VString;
18087  if(HLoc < 0)
18088  {
18089  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18090  }
18091  else
18092  {
18093  HString = AnsiString(HLoc);
18094  }
18095  if(VLoc < 0)
18096  {
18097  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18098  }
18099  else
18100  {
18101  VString = AnsiString(VLoc);
18102  }
18103  ServiceCallingLocsList.push_back(HString + '-' + VString);
18104  }
18105  }
18106  }
18107  else if(AVE.SequenceType == StartSequence) //other start entries, all located
18108  {
18109  ServiceCallingLocsList.push_back(AVE.LocationName);
18110  }
18111  else if(AVE.FormatType == TimeLoc) //z must be > 0
18112  {
18113  if(ServiceCallingLocsList.back() != AVE.LocationName)
18114  {
18115  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
18116  }
18117  }
18118  else if(AVE.FormatType == PassTime)
18119  {
18120  ServiceCallingLocsList.push_back(AVE.LocationName);
18121  }
18122  else if(AVE.FormatType == TimeTimeLoc)
18123  {
18124  ServiceCallingLocsList.push_back(AVE.LocationName);
18125  }
18126  else if(AVE.Command == "cdt") //list if not next to start or finish
18127  {
18128  if(ActionVector.at(z-1).SequenceType == StartSequence)
18129  {
18130  continue;
18131  }
18132  else if(ActionVector.at(z+1).SequenceType == FinishSequence) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
18133  {
18134  continue;
18135  }
18136  else
18137  {
18138  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
18139  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
18140  }
18141  }
18142  else if(AVE.FormatType == ExitRailway) //Fer
18143  {
18144  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
18145  AnsiString LName = TE.ActiveTrackElementName;
18146  if(LName != "")
18147  {
18148  ServiceCallingLocsList.push_back(LName);
18149  }
18150  else
18151  {
18152  int HLoc = TE.HLoc;
18153  int VLoc = TE.VLoc;
18154  AnsiString HString;
18155  AnsiString VString;
18156  if(HLoc < 0)
18157  {
18158  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18159  }
18160  else
18161  {
18162  HString = AnsiString(HLoc);
18163  }
18164  if(VLoc < 0)
18165  {
18166  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18167  }
18168  else
18169  {
18170  VString = AnsiString(VLoc);
18171  }
18172  ServiceCallingLocsList.push_back(HString + '-' + VString);
18173  }
18174  }
18175  }
18176  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
18177  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
18178  }
18179  //AllServiceCallingLocsMap built
18180  SequenceLog += "2\n";
18181 /*
18182 // this sequence is to test the validity of AllServiceCallingLocsMap
18183  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
18184  std::ofstream Test(TestFile.c_str());
18185 
18186  if(TestFile == 0)
18187  {
18188  ShowMessage("TestFile failed to open - can't be created");
18189  Utilities->CallLogPop();
18190  return false;
18191  }
18192 
18193  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
18194  {
18195  Test << ASCLIt->first << '\n'; //service ref
18196  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
18197  {
18198  Test << *SCLIt << '\n';
18199  }
18200  Test << "\n\n";
18201  }
18202  Test.close();
18203  Utilities->CallLogPop();
18204  return true;
18205 */
18206  //initialise variables before calc LastTTTime & build LocServiceTimesVector
18207  if(TrainDataVectorCopy.empty())
18208  {
18209  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
18210  Utilities->CallLogPop(2209);
18211  return(false);
18212  }
18213  TLocServiceTimes TLSTEntry;
18214  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
18215  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
18216  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
18217  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
18218  LastTTTime = "";
18219  SequenceLog += "3\n";
18220  //calculate LastTTTime
18221  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
18222  {
18223  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
18224  TActionVector &ActionVector = TrainDataEntry.ActionVector;
18225  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
18226  TDateTime LastTDTime;
18227  int IncMinutes = 0;
18228  NumTrains = TrainDataEntry.NumberOfTrains;
18229  if(ActionVector.empty())
18230  {
18231  continue;
18232  }
18233  if(ActionVector.at(0).SignallerControl)
18234  {
18235  continue;
18236  }
18237  if(AVLast->FormatType == Repeat)
18238  {
18239  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
18240  AVLast--; //now points to the command before the repeat
18241  }
18242  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
18243  {
18244  AVLast--; //points to last timed entry
18245  }
18246  //here AVLast points to last entry with a time
18247  if(AVLast->ArrivalTime != TDateTime(-1))
18248  {
18249  LastTDTime = AVLast->ArrivalTime;
18250  }
18251  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
18252  {
18253  LastTDTime = AVLast->EventTime;
18254  }
18255  else
18256  {
18257  continue; //shouldn't ever reach here but if do then skip this service
18258  }
18259  if(NumTrains == 1)
18260  {
18261  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
18262  }
18263  else
18264  {
18265  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
18266  }
18267  if(LastAnsiTime > LastTTTime)
18268  {
18269  LastTTTime = LastAnsiTime;
18270  }
18271  }
18272  SequenceLog += "4\n";
18273 //build LocServiceTimesVector
18274 
18275 /*
18276  struct TLocServiceTimes
18277  {
18278  AnsiString Location;
18279  AnsiString ServiceAndRepeatNum;
18280  AnsiString AtLocTime;
18281  AnsiString ArrTime;
18282  AnsiString DepTime;
18283  AnsiString FrhMarker;
18284  };
18285  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
18286 
18287 This works as follows:
18288 ServiceAndRepeatNum is taken from the TrainDataVectorCopy as it is the same for all actionvector entries
18289 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
18290 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
18291 
18292 Every action for every train is examined and times entered as follows:-
18293 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
18294 b) an unlocated Snt: entry time becomes DepTime
18295 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
18296 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
18297 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
18298 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
18299 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
18300 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
18301 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
18302 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
18303 */
18304  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
18305  {
18306  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
18307  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
18308  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
18309  int IncMinutes = 0;
18310  NumTrains = TrainDataEntry.NumberOfTrains;
18311  if(ActionVector.empty())
18312  {
18313  continue;
18314  }
18315  if(ActionVector.at(0).SignallerControl)
18316  {
18317  continue;
18318  }
18319  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
18320  {
18321  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
18322  }
18323  for(int y = 0; y < NumTrains; y++) //y is the repeat number
18324  {
18325  if(NumTrains == 1)
18326  {
18327  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
18328  }
18329  else if(y == 0)
18330  {
18331  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
18332  }
18333  else
18334  {
18335  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
18336  }
18337  for(unsigned int z = 0; z < ActionVector.size(); z++)
18338  {
18339  TActionVectorEntry AVE = ActionVector.at(z);
18340  TLSTEntry.AtLocTime = "";
18341  TLSTEntry.ArrTime = "";
18342  TLSTEntry.DepTime = "";
18343  TLSTEntry.Location = "";
18344  TLSTEntry.FrhMarker = "";
18345 
18346  if(AVE.FormatType == StartNew) //Snt only
18347  {
18348  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
18349  {
18350  TLSTEntry.Location = AVE.LocationName;
18351  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
18352  LocServiceTimesVector.push_back(TLSTEntry);
18353 
18354  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
18355  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
18356  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
18357  {
18358  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
18359  {
18360  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
18361  break;
18362  }
18363  if(ActionVector.at(a).SequenceType == FinishSequence) //finish catered in a later test
18364  {
18365  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
18366  break;
18367  }
18368  }
18369  if(FoundStopTime == "")
18370  {
18371  throw Exception("Failure to determine FoundStopTime for located Snt");
18372  }
18373  int WhileCount = 0;
18374  while(true)
18375  {
18376  //add minutes until reach FoundStopTime but don't add that time
18377  WhileCount++;
18378  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18379  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
18380  TLSTEntry.DepTime = "";
18381  TLSTEntry.ArrTime = "";
18382  if(IncTime >= FoundStopTime) //don't add that time
18383  {
18384  break;
18385  }
18386  LocServiceTimesVector.push_back(TLSTEntry);
18387  if(WhileCount > 2000)
18388  {
18389  throw Exception("While loop failed to break in 2000 loops for located Snt");
18390  }
18391  }
18392  }
18393  else //unlocated Snt, use the EventTime as DepTime for this vector
18394  {
18396  if(TE.ActiveTrackElementName != "")
18397  {
18398  TLSTEntry.Location = TE.ActiveTrackElementName;
18399  }
18400  else
18401  {
18402  int HLoc = TE.HLoc;
18403  int VLoc = TE.VLoc;
18404  AnsiString HString;
18405  AnsiString VString;
18406  if(HLoc < 0)
18407  {
18408  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18409  }
18410  else
18411  {
18412  HString = AnsiString(HLoc);
18413  }
18414  if(VLoc < 0)
18415  {
18416  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18417  }
18418  else
18419  {
18420  VString = AnsiString(VLoc);
18421  }
18422  TLSTEntry.Location = HString + '-' + VString;
18423  }
18424  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
18425  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
18426  LocServiceTimesVector.push_back(TLSTEntry);
18427  }
18428  }
18429 
18430  else if(AVE.SequenceType == StartSequence) //other start entries, all located
18431  {
18432  TLSTEntry.Location = AVE.LocationName;
18433  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
18434  LocServiceTimesVector.push_back(TLSTEntry);
18435  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
18436  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
18437  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
18438  {
18439  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
18440  {
18441  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
18442  break;
18443  }
18444  if(ActionVector.at(a).SequenceType == FinishSequence) //finish catered in a later test
18445  {
18446  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
18447  break;
18448  }
18449  }
18450  if(FoundStopTime == "")
18451  {
18452  throw Exception("Failure to determine FoundStopTime for SequenceType == StartSequence");
18453  }
18454  int WhileCount = 0;
18455  while(true)
18456  {
18457  //add minutes until reach FoundStopTime but don't add that time
18458  WhileCount++;
18459  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18460  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
18461  TLSTEntry.DepTime = "";
18462  TLSTEntry.ArrTime = "";
18463  if(IncTime >= FoundStopTime) //don't add that time
18464  {
18465  break;
18466  }
18467  LocServiceTimesVector.push_back(TLSTEntry);
18468  if(WhileCount > 2000)
18469  {
18470  throw Exception("While loop failed to break in 2000 loops for SequenceType == StartSequence");
18471  }
18472  }
18473  }
18474 
18475  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
18476  {
18477  TLSTEntry.Location = AVE.LocationName;
18478  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
18479  {
18480  bool SkipAddingMinutes = false;
18481  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
18482  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
18483  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
18484  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
18485  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
18486  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
18487  {
18488  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
18489  {
18490  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
18491  break;
18492  }
18493  if(ActionVector.at(a).SequenceType == FinishSequence) //finish catered for in a later test
18494  {
18495  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
18496  if((a <= (z + 2)) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr != NULL) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr != NULL)))
18497  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
18498  //at v2.10.0 changed (a == z + 1) to (a <= (z + 2)) as can have a cdt between, this allows for that
18499  {
18500  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
18501  SkipAddingMinutes = true;
18502  }
18503  break;
18504  }
18505  }
18506  if(FoundStopTime == "")
18507  {
18508  throw Exception("Failure to determine FoundStopTime for SequenceType == StartSequence");
18509  }
18510  if(!SkipAddingMinutes)
18511  {
18512  int WhileCount = 0;
18513  while(true)
18514  {
18515  //add minutes until reach FoundStopTime but don't add that time
18516  WhileCount++;
18517  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18518  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
18519  TLSTEntry.DepTime = "";
18520  TLSTEntry.ArrTime = "";
18521  if(IncTime >= FoundStopTime) //don't add that time
18522  {
18523  break;
18524  }
18525  LocServiceTimesVector.push_back(TLSTEntry);
18526  if(WhileCount > 2000)
18527  {
18528  throw Exception("While loop failed to break in 2000 loops for SequenceType == StartSequence");
18529  }
18530  }
18531  }
18532  }
18533  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
18534  {
18535  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
18536  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
18537  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
18538  {
18539  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
18540  {
18541  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
18542  LocServiceTimesVector.pop_back();
18543  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
18544  }
18545  else //just add the dep & atloc times
18546  {
18547  TLSTEntry.ArrTime = "";
18548  LocServiceTimesVector.push_back(TLSTEntry);
18549  }
18550  }
18551  else //just add the dep & atloc times
18552  {
18553  TLSTEntry.ArrTime = "";
18554  LocServiceTimesVector.push_back(TLSTEntry);
18555  }
18556  }
18557  }
18558 
18559  else if(AVE.FormatType == TimeTimeLoc)
18560  {
18561  TLSTEntry.Location = AVE.LocationName;
18562  if(AVE.ArrivalTime > TDateTime(-1)) //should be
18563  {
18564  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
18565  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
18566  }
18567  if(AVE.DepartureTime > TDateTime(-1)) //should be
18568  {
18569  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
18570  }
18571  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
18572  {
18573  LocServiceTimesVector.push_back(TLSTEntry);
18574  }
18575  else
18576  {
18577  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
18578  TLSTEntry.DepTime = "";
18579  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
18580  TLSTEntry.ArrTime = ""; //done with this now
18581  while(TLSTEntry.AtLocTime < TempDepTime)
18582  {
18583  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18584  if(TLSTEntry.AtLocTime == TempDepTime)
18585  {
18586  TLSTEntry.DepTime = TempDepTime; //restore value
18587  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
18588  }
18589  else
18590  {
18591  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
18592  }
18593  }
18594  }
18595  }
18596 
18597  else if(AVE.FormatType == PassTime) //added at v2.9.1
18598  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
18599  TLSTEntry.Location = AVE.LocationName;;
18600  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
18601  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
18602  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
18603  TLSTEntry.ArrTime = ""; //need to reset this to null
18604  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
18605  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
18606  }
18607 
18608  else if(AVE.FormatType == ExitRailway) //Fer
18609  {
18610  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
18611  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
18612  //be wrong, but can't guess from here & most will have same name
18613  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
18614  if(LName != "")
18615  {
18616  TLSTEntry.Location = LName;
18617  }
18618  else
18619  {
18620  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
18621  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
18622  AnsiString HString;
18623  AnsiString VString;
18624  if(HLoc < 0)
18625  {
18626  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18627  }
18628  else
18629  {
18630  HString = AnsiString(HLoc);
18631  }
18632  if(VLoc < 0)
18633  {
18634  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18635  }
18636  else
18637  {
18638  VString = AnsiString(VLoc);
18639  }
18640  TLSTEntry.Location = HString + '-' + VString;
18641  }
18642  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
18643  }
18644 
18645  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
18646  {
18647  AnsiString FrhTime;
18648  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
18649  {
18650  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
18651  }
18652  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
18653  {
18654  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
18655  }
18656  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
18657  TLSTEntry.Location = AVE.LocationName;
18658  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18659  TLSTEntry.FrhMarker = "Frh";
18660  LocServiceTimesVector.push_back(TLSTEntry);
18661  TLSTEntry.FrhMarker = "";
18662  //add all times from next minute to end of timetable
18663  while(IncTime <= LastTTTime)
18664  {
18665  TLSTEntry.AtLocTime = IncTime;
18666  LocServiceTimesVector.push_back(TLSTEntry);
18667  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
18668  }
18669  }
18670 
18671  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
18672  {
18673  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
18674  {
18675  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
18676  TLSTEntry.Location = AVE.LocationName;
18677  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18678  TLSTEntry.FrhMarker = "Frh";
18679  LocServiceTimesVector.push_back(TLSTEntry);
18680  TLSTEntry.FrhMarker = "";
18681  //add all times from next minute to end of timetable
18682  while(IncTime <= LastTTTime)
18683  {
18684  TLSTEntry.AtLocTime = IncTime;
18685  LocServiceTimesVector.push_back(TLSTEntry);
18686  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
18687  }
18688  }
18689  }
18690 
18691  else if(AVE.SequenceType == FinishSequence) //other finish types - all located & all link to another service
18692  {
18693  //nothing is done here as the entry will be listed at this time under the new service reference
18694  }
18695  }
18696  }
18697  }
18698  SequenceLog += "5\n";
18699  //now sort in location order
18700  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
18701  //LocServiceTimesVector now complete & sorted in location order
18702 
18703 /*
18704 //start of debugging section
18705 //create LocServiceTimesVector output file for debugging purposes
18706  AnsiString LSTVTestFile = CurDir + "\\Formatted timetables\\LSTVTestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
18707  std::ofstream LSTVFile(LSTVTestFile.c_str());
18708  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
18709  {
18710  LSTVFile << LSTVIt->Location + '\n';
18711  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
18712  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
18713  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
18714  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
18715  if(LSTVIt->FrhMarker == "")
18716  {
18717  LSTVFile << "Not Frh\n";
18718  }
18719  else
18720  {
18721  LSTVFile << LSTVIt->FrhMarker + '\n';
18722  }
18723  LSTVFile << '\n';
18724  }
18725  LSTVFile.close();
18726  Utilities->CallLogPop();
18727  return(true);
18728 //end of debugging section
18729 */
18730  //declare pointers for use in printouts
18731  TLocServiceTimesVector::iterator Ptr1, Ptr2;
18732 
18733  //set up the output file
18734  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
18735  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
18736 
18737  std::ofstream TTFile3(TTFileName3.c_str());
18738 
18739  if(TTFile3 == 0)
18740  {
18741  ShowMessage("Conflict Analysis file failed to open - can't be created");
18742  Utilities->CallLogPop(2210);
18743  return(false);
18744  }
18745  if(LocServiceTimesVector.empty())
18746  {
18747  ShowMessage("No timetabled services found");
18748  TTFile3.close();
18749  DeleteFile(TTFileName3);
18750  Utilities->CallLogPop(2211);
18751  return(false);
18752  }
18753  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n";
18754  TTFile3 << "See user manual or on-screen help section 5.12 for detailed information.\n\n\n";
18755  SequenceLog += "6\n";
18756 
18757 /*
18758 //print out TrainDataVectorCopy & TrainDataVector for debugging purposes, TrainDataVectorCopy first
18759 
18760 // Double crosslink (shuttle) table:
18761 //Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
18762 // Code ShuttleLink- EntryPtr ShuttleLink-
18763 // HeadCode EntryPtr
18764 
18765 //Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
18766 //Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
18767 //F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
18768 //Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
18769 //Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
18770 //Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
18771 //
18772 //Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
18773 
18774 //NOTE: from above for F-nshs & Sns-fsh in the TrainDataVectorCopy the OtherLinkedHeadCode will be correct as it is derived from LinkTrainEntryPtr which is correct
18775 //but for the original TrainDataVector the OtherLinkedHeadCode will be incorrect, hence have to use the NonRepeatingShuttleLinkHeadCode as that is the correct one
18776 //these were errors when first coded but work ok, just keep in mind when making any changes
18777 
18778 std::ofstream TDVCFile((CurDir + "\\Formatted timetables\\Conflict Analysis " + TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss") + "; " + RailwayTitle + "; " + TimetableTitle + " TrainDataVectorCopy.txt").c_str());
18779 AnsiString OHC = "", NRHC = "";
18780 AnsiString OLk = "", NRLk = "";
18781 TDVCFile << "Note that for the TrainDataVectorCopy OH and OLk should always be the same, as OH is derived from OHLk; and similarly for NR and NRLk\n\n";
18782 for(TTrainDataVector::iterator TDVCIt = TrainDataVectorCopy.begin(); TDVCIt != TrainDataVectorCopy.end(); TDVCIt++)
18783 {
18784  TDVCFile << TDVCIt->ServiceReference + '\n';
18785  TDVCFile << TDVCIt->Description + '\n';
18786  for(unsigned int x = 0; x < TDVCIt->ActionVector.size(); x++)
18787  {
18788  TActionVectorEntry AVE = TDVCIt->ActionVector.at(x);
18789  if(AVE.OtherHeadCode == "")
18790  {
18791  OHC = "OH 0";
18792  }
18793  else
18794  {
18795  OHC = "OH " + AVE.OtherHeadCode;
18796  }
18797  if(AVE.NonRepeatingShuttleLinkHeadCode == "")
18798  {
18799  NRHC = "NR 0";
18800  }
18801  else
18802  {
18803  NRHC = "NR " + AVE.NonRepeatingShuttleLinkHeadCode;
18804  }
18805  if(TDVCIt->ActionVector.at(x).LinkedTrainEntryPtr == 0)
18806  {
18807  OLk = "OLk 0";
18808  }
18809  else
18810  {
18811  OLk = "OLk " + TDVCIt->ActionVector.at(x).LinkedTrainEntryPtr->ServiceReference;
18812  }
18813  if(TDVCIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr == 0)
18814  {
18815  NRLk = "NRLk 0";
18816  }
18817  else
18818  {
18819  NRLk = "NRLk " + TDVCIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18820  }
18821 
18822  if(AVE.FormatType == TimeCmd) //cdt only
18823  {
18824  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << '\n';
18825  }
18826  if((AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle))
18827  {
18828  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18829  }
18830  else if((AVE.FormatType == FSHNewService) || (AVE.FormatType == SNSShuttle)) //these should have 2 linked services
18831  {
18832  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18833  }
18834  else if(AVE.FormatType == SNSNonRepeatFromShuttle)
18835  {
18836  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18837  }
18838  else if(AVE.FormatType == StartNew)
18839  {
18840  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt RearStartID " << Track->TrackElementAt(-1, AVE.RearStartOrRepeatMins).ElementID
18841  << " FrontStartID " << Track->TrackElementAt(-2, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18842  }
18843  else if(AVE.FormatType == SNTShuttle)
18844  {
18845  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt-sh RearStartID " << Track->TrackElementAt(-3, AVE.RearStartOrRepeatMins).ElementID
18846  << " FrontStartID " << Track->TrackElementAt(-4, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18847  }
18848  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18849  {
18850  TDVCFile << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
18851  }
18852  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18853  {
18854  TDVCFile << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
18855  }
18856  else if(AVE.FormatType == TimeTimeLoc)
18857  {
18858  TDVCFile << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
18859  }
18860  else if(AVE.FormatType == PassTime)
18861  {
18862  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18863  }
18864  else if(AVE.FormatType == ExitRailway)
18865  {
18866  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << " Fer" << '\n';
18867  }
18868  else if(AVE.FormatType == FinRemHere)
18869  {
18870  TDVCFile << "Frh" << '\n';
18871  }
18872  }
18873  TDVCFile << '\n';
18874 }
18875 TDVCFile.close();
18876 
18877 //print out original TrainDataVector for comparison
18878 std::ofstream TDVFile((CurDir + "\\Formatted timetables\\Conflict Analysis " + TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss") + "; " + RailwayTitle + "; " + TimetableTitle + " TrainDataVector.txt").c_str());
18879 TDVFile << "Note that in the TrainDataVector, non-repeating shuttle link services F-nshs and Sns-fsh use the non-repeating headcode (NR) values for the corresponding "
18880  "shuttle headcodes when it should be the other headcode (OH), and the other headcode is unused. The link values are the right way round. Also OH & NR "
18881  "values ARE headcodes and not service references, but OLk and NRLk values are service references.\n\n";
18882 //F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
18883 //Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
18884 for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18885 {
18886  TDVFile << TDVIt->ServiceReference + '\n';
18887  TDVFile << TDVIt->Description + '\n';
18888  for(unsigned int x = 0; x < TDVIt->ActionVector.size(); x++)
18889  {
18890  TActionVectorEntry AVE = TDVIt->ActionVector.at(x);
18891  if(AVE.OtherHeadCode == "")
18892  {
18893  OHC = "OH 0";
18894  }
18895  else
18896  {
18897  OHC = "OH " + AVE.OtherHeadCode;
18898  }
18899  if(AVE.NonRepeatingShuttleLinkHeadCode == "")
18900  {
18901  NRHC = "NR 0";
18902  }
18903  else
18904  {
18905  NRHC = "NR " + AVE.NonRepeatingShuttleLinkHeadCode;
18906  }
18907  if(TDVIt->ActionVector.at(x).LinkedTrainEntryPtr == 0)
18908  {
18909  OLk = "OLk 0";
18910  }
18911  else
18912  {
18913  OLk = "OLk " + TDVIt->ActionVector.at(x).LinkedTrainEntryPtr->ServiceReference;
18914  }
18915  if(TDVIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr == 0)
18916  {
18917  NRLk = "NRLk 0";
18918  }
18919  else
18920  {
18921  NRLk = "NRLk " + TDVIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18922  }
18923 
18924  if(AVE.FormatType == TimeCmd) //cdt only
18925  {
18926  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << '\n';
18927  }
18928  if((AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle))
18929  {
18930  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18931  }
18932  else if((AVE.FormatType == FSHNewService) || (AVE.FormatType == SNSShuttle)) //these should have 2 linked services
18933  {
18934  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18935  }
18936  else if(AVE.FormatType == SNSNonRepeatFromShuttle)
18937  {
18938  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18939  }
18940  else if(AVE.FormatType == StartNew)
18941  {
18942  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt RearStartID " << Track->TrackElementAt(-5, AVE.RearStartOrRepeatMins).ElementID
18943  << " FrontStartID " << Track->TrackElementAt(-6, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18944  }
18945  else if(AVE.FormatType == SNTShuttle)
18946  {
18947  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt-sh RearStartID " << Track->TrackElementAt(-7, AVE.RearStartOrRepeatMins).ElementID
18948  << " FrontStartID " << Track->TrackElementAt(-8, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18949  }
18950  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18951  {
18952  TDVFile << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
18953  }
18954  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18955  {
18956  TDVFile << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
18957  }
18958  else if(AVE.FormatType == TimeTimeLoc)
18959  {
18960  TDVFile << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
18961  }
18962  else if(AVE.FormatType == PassTime)
18963  {
18964  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18965  }
18966  else if(AVE.FormatType == ExitRailway)
18967  {
18968  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << " Fer" << '\n';
18969  }
18970  else if(AVE.FormatType == FinRemHere)
18971  {
18972  TDVFile << "Frh" << '\n';
18973  }
18974  }
18975  TDVFile << '\n';
18976 }
18977 TDVFile.close();
18978 //end of debugging
18979 */
18980  //arrivals
18981  if(ArrChecked)
18982  {
18983  //sort in ArrTime order for each location
18984  Ptr1 = LocServiceTimesVector.begin();
18985  Ptr2 = Ptr1 + 1;
18986  while(Ptr2 != LocServiceTimesVector.end())
18987  {
18988  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
18989  {
18990  Ptr2++;
18991  if(Ptr2 == LocServiceTimesVector.end())
18992  {
18993  break;
18994  }
18995  }
18996  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
18997  Ptr1 = Ptr2; //first entry with next name
18998  if(Ptr2 != LocServiceTimesVector.end())
18999  {
19000  Ptr2++;
19001  }
19002  }
19003 
19004  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
19005 
19006  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
19007  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
19008  MinuteString = " minutes";
19009  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
19010  if(ArrRange == 1)
19011  {
19012  MinuteString = " minute";
19013  }
19014  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
19015  TTFile3 << ",Platforms,Trains\n\n";
19016 
19017  Ptr1 = LocServiceTimesVector.begin();
19018  Ptr2 = Ptr1 + 1;
19019  while(Ptr2 != LocServiceTimesVector.end())
19020  {
19021  PreviousService = "";
19022  NumTrainsAtLoc = 0;
19023  ServiceAndRepeatNumTotal = "";
19024  NumPlats = 0;
19025  NumPlatsAtThisLocCalculated = false;
19026  BasicTime = "";
19027  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
19028  {
19029  PreviousService = "";
19030  NumTrainsAtLoc = 0;
19031  ServiceAndRepeatNumTotal = "";
19032  NumPlats = 0;
19033  NumPlatsAtThisLocCalculated = false;
19034  BasicTime = "";
19035  Ptr1++;
19036  Ptr2++;
19037  if(Ptr2 == LocServiceTimesVector.end())
19038  {
19039  break;
19040  }
19041  }
19042  if(Ptr2 == LocServiceTimesVector.end())
19043  {
19044  break;
19045  }
19046  while(Ptr2->Location == Ptr1->Location)
19047  {
19048  PreviousService = "";
19049  NumTrainsAtLoc = 0;
19050  ServiceAndRepeatNumTotal = "";
19051  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
19052  if((Ptr1->Location == "") && (Ptr2->Location == ""))
19053  {
19054  break;
19055  }
19056  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
19057  {
19058  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
19059  Ptr1++;
19060  Ptr2++;
19061  if(Ptr2 == LocServiceTimesVector.end())
19062  {
19063  break;
19064  }
19065  if(Ptr2->Location != Ptr1->Location)
19066  {
19067  break;
19068  }
19069  }
19070  if(Ptr2 == LocServiceTimesVector.end())
19071  {
19072  break;
19073  }
19074  if(Ptr2->Location != Ptr1->Location)
19075  {
19076  break;
19077  }
19078  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
19079  {
19080  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
19081  {
19082  break;
19083  }
19084  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
19085  {
19086  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
19087  NumPlatsAtThisLocCalculated = true;
19088  }
19089  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
19090  {
19091  if(ServiceAndRepeatNumTotal == "")
19092  {
19093  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
19094  NumTrainsAtLoc = 1;
19095  }
19096  else
19097  {
19098  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
19099  }
19100  }
19101  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
19102  if(ServiceAndRepeatNumTotal == "")
19103  {
19104  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
19105  NumTrainsAtLoc = 1;
19106  }
19107  else
19108  {
19109  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
19110  }
19111  Ptr1 = Ptr2;
19112  Ptr2++;
19113  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
19114  {
19115  int MaxNumberOfSameDirections = 0;
19116  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
19117  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
19118  {
19119 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
19120  TTFile3.close();
19121  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
19122 // Utilities->CallLogPop(2224);
19123 // return false;
19124  }
19125  AnsiString Asterisk = "";
19126  if(MaxNumberOfSameDirections >= NumPlats)
19127  {
19128  Asterisk = "* ";
19129  }
19130  //print out a single line for number of trains at loc with all service refs
19131  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
19132  ArrivalsPrinted = true;
19133  ServiceAndRepeatNumTotal = "";
19134  }
19135  if(Ptr2 == LocServiceTimesVector.end())
19136  {
19137  break;
19138  }
19139  if(Ptr2->Location != Ptr1->Location)
19140  {
19141  break;
19142  }
19143  }
19144  if(Ptr2 == LocServiceTimesVector.end())
19145  {
19146  break;
19147  }
19148  }
19149  }
19150  if(!ArrivalsPrinted)
19151  {
19152  TTFile3 << "Nothing to report for arrivals";
19153  }
19154  TTFile3 << "\n\n";
19155  }
19156  //end of routine for arrivals
19157  SequenceLog += "7\n";
19158  //departures
19159  if(DepChecked)
19160  {
19161  //sort in DepTime order for each location
19162  Ptr1 = LocServiceTimesVector.begin();
19163  Ptr2 = Ptr1 + 1;
19164  while(Ptr2 != LocServiceTimesVector.end())
19165  {
19166  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
19167  {
19168  Ptr2++;
19169  if(Ptr2 == LocServiceTimesVector.end())
19170  {
19171  break;
19172  }
19173  }
19174  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
19175  Ptr1 = Ptr2; //first entry with next name
19176  if(Ptr2 != LocServiceTimesVector.end())
19177  {
19178  Ptr2++;
19179  }
19180  }
19181 
19182  //routine for departures - number of trains departing within the specified range with services listed at the end
19183  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
19184  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
19185  MinuteString = " minutes";
19186  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
19187  if(DepRange == 1)
19188  {
19189  MinuteString = " minute";
19190  }
19191  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
19192  TTFile3 << ",Platforms,Trains\n\n";
19193 
19194  Ptr1 = LocServiceTimesVector.begin();
19195  Ptr2 = Ptr1 + 1;
19196  while(Ptr2 != LocServiceTimesVector.end())
19197  {
19198  PreviousService = "";
19199  NumTrainsAtLoc = 0;
19200  ServiceAndRepeatNumTotal = "";
19201  NumPlats = 0;
19202  NumPlatsAtThisLocCalculated = false;
19203  BasicTime = "";
19204  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
19205  {
19206  PreviousService = "";
19207  NumTrainsAtLoc = 0;
19208  ServiceAndRepeatNumTotal = "";
19209  NumPlats = 0;
19210  NumPlatsAtThisLocCalculated = false;
19211  BasicTime = "";
19212  Ptr1++;
19213  Ptr2++;
19214  if(Ptr2 == LocServiceTimesVector.end())
19215  {
19216  break;
19217  }
19218  }
19219  if(Ptr2 == LocServiceTimesVector.end())
19220  {
19221  break;
19222  }
19223  while(Ptr2->Location == Ptr1->Location)
19224  {
19225  PreviousService = "";
19226  NumTrainsAtLoc = 0;
19227  ServiceAndRepeatNumTotal = "";
19228  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
19229  if((Ptr1->Location == "") && (Ptr2->Location == ""))
19230  {
19231  break;
19232  }
19233  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
19234  {
19235  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
19236  Ptr1++;
19237  Ptr2++;
19238  if(Ptr2 == LocServiceTimesVector.end())
19239  {
19240  break;
19241  }
19242  if(Ptr2->Location != Ptr1->Location)
19243  {
19244  break;
19245  }
19246  }
19247  if(Ptr2 == LocServiceTimesVector.end())
19248  {
19249  break;
19250  }
19251  if(Ptr2->Location != Ptr1->Location)
19252  {
19253  break;
19254  }
19255  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
19256  {
19257  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
19258  {
19259  break;
19260  }
19261  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
19262  {
19263  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
19264  NumPlatsAtThisLocCalculated = true;
19265  }
19266  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
19267  {
19268  if(ServiceAndRepeatNumTotal == "")
19269  {
19270  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
19271  NumTrainsAtLoc = 1;
19272  }
19273  else
19274  {
19275  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
19276  }
19277  }
19278  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
19279  if(ServiceAndRepeatNumTotal == "")
19280  {
19281  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
19282  NumTrainsAtLoc = 1;
19283  }
19284  else
19285  {
19286  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
19287  }
19288  Ptr1 = Ptr2;
19289  Ptr2++;
19290  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
19291  {
19292  int MaxNumberOfSameDirections = 0;
19293  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
19294  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
19295  {
19296 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
19297  TTFile3.close();
19298  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
19299 // Utilities->CallLogPop(2225);
19300 // return false;
19301  }
19302  AnsiString Asterisk = "";
19303  if(MaxNumberOfSameDirections >= NumPlats)
19304  {
19305  Asterisk = "* ";
19306  }
19307  //print out a single line for number of trains at loc with all service refs
19308  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
19309  DeparturesPrinted = true;
19310  ServiceAndRepeatNumTotal = "";
19311  }
19312  if(Ptr2 == LocServiceTimesVector.end())
19313  {
19314  break;
19315  }
19316  if(Ptr2->Location != Ptr1->Location)
19317  {
19318  break;
19319  }
19320  }
19321  if(Ptr2 == LocServiceTimesVector.end())
19322  {
19323  break;
19324  }
19325  }
19326  }
19327  if(!DeparturesPrinted)
19328  {
19329  TTFile3 << "Nothing to report for departures";
19330  }
19331  TTFile3 << "\n\n";
19332  }
19333  //end of routine for departures
19334  SequenceLog += "8\n";
19335 
19336  //list trains at locations at same time
19337 
19338  if(AtLocChecked)
19339  {
19340  //sort in AtLocTime order for each location
19341  Ptr1 = LocServiceTimesVector.begin();
19342  Ptr2 = Ptr1 + 1;
19343  while(Ptr2 != LocServiceTimesVector.end())
19344  {
19345  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
19346  {
19347  Ptr2++;
19348  if(Ptr2 == LocServiceTimesVector.end())
19349  {
19350  break;
19351  }
19352  }
19353  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
19354  Ptr1 = Ptr2; //first entry with next name
19355  if(Ptr2 != LocServiceTimesVector.end())
19356  {
19357  Ptr2++;
19358  }
19359  }
19360 
19361  //print out simultaneous AtLocs (don't need range of times for AtLocs)
19362  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
19363  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
19364  TTFile3 << ",Platforms,Trains,\n\n";
19365  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
19366  Ptr1 = LocServiceTimesVector.begin();
19367  Ptr2 = Ptr1 + 1;
19368  while(Ptr2 != LocServiceTimesVector.end())
19369  {
19370  PreviousService = "";
19371  ServiceAndRepeatNumTotal = "";
19372  NumTrainsAtLoc = 0;
19373  NumPlats = 0;
19374  NumPlatsAtThisLocCalculated = false;
19375  FrhCount = 0;
19376 
19377  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
19378  {
19379  PreviousService = "";
19380  ServiceAndRepeatNumTotal = "";
19381  NumTrainsAtLoc = 0;
19382  NumPlats = 0;
19383  NumPlatsAtThisLocCalculated = false;
19384  FrhCount = 0;
19385  Ptr1++;
19386  Ptr2++;
19387  if(Ptr2 == LocServiceTimesVector.end())
19388  {
19389  break;
19390  }
19391  }
19392  if(Ptr2 == LocServiceTimesVector.end())
19393  {
19394  break;
19395  }
19396  while(Ptr2->Location == Ptr1->Location)
19397  {
19398  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
19399  {
19400  FrhCount++;
19401  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
19402  }
19403  PreviousService = "";
19404  NumTrainsAtLoc = 0;
19405  ServiceAndRepeatNumTotal = "";
19406  if((Ptr1->Location == "") && (Ptr2->Location == ""))
19407  {
19408  break;
19409  }
19410  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
19411  {
19412  Ptr1++;
19413  if(Ptr1->FrhMarker == "Frh")
19414  {
19415  FrhCount++;
19416  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
19417  }
19418  Ptr2++;
19419  if(Ptr2 == LocServiceTimesVector.end())
19420  {
19421  break;
19422  }
19423  if(Ptr2->Location != Ptr1->Location)
19424  {
19425  break;
19426  }
19427  }
19428  if(Ptr2 == LocServiceTimesVector.end())
19429  {
19430  break;
19431  }
19432  if(Ptr2->Location != Ptr1->Location)
19433  {
19434  break;
19435  }
19436  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
19437  {
19438  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
19439  {
19440  break;
19441  }
19442  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
19443  {
19444  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
19445  NumPlatsAtThisLocCalculated = true;
19446  }
19447  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
19448  {
19449  if(ServiceAndRepeatNumTotal == "")
19450  {
19451  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
19452  NumTrainsAtLoc = 1;
19453  }
19454  else
19455  {
19456  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
19457  }
19458  }
19459  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
19460  if(ServiceAndRepeatNumTotal == "")
19461  {
19462  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
19463  NumTrainsAtLoc = 1;
19464  }
19465  else
19466  {
19467  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
19468  }
19469  Ptr1 = Ptr2;
19470  if(Ptr1->FrhMarker == "Frh")
19471  {
19472  FrhCount++;
19473  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
19474  }
19475  Ptr2++;
19476  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
19477  {
19478 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
19479 //new text //don't print out if all remainers or if only 1 train at loc
19480  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
19481 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
19482 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
19483  {
19484  AnsiString Asterisk = "";
19485  if(NumTrainsAtLoc > NumPlats)
19486  {
19487  Asterisk = "* ";
19488  }
19489  //print out a single line for number of trains at loc with all service refs
19490  if(FrhCount == 0)
19491  {
19492  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
19493  }
19494  else if(FrhCount == 1)
19495  {
19496  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
19497  }
19498  else
19499  {
19500  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
19501  }
19502  LastFrhCount = FrhCount;
19503  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
19504  AtLocsPrinted = true;
19505  ServiceAndRepeatNumTotal = "";
19506  }
19507  }
19508  if(Ptr2 == LocServiceTimesVector.end())
19509  {
19510  break;
19511  }
19512  if(Ptr2->Location != Ptr1->Location)
19513  {
19514  break;
19515  }
19516  }
19517  if(Ptr2 == LocServiceTimesVector.end())
19518  {
19519  break;
19520  }
19521  }
19522  }
19523  if(!AtLocsPrinted)
19524  {
19525  TTFile3 << "Nothing to report for trains at locations";
19526  }
19527  TTFile3 << "\n\n";
19528  //end of simultaneous AtLocs
19529  }
19530  SequenceLog += "9\n";
19531 
19532 /*
19533 //start of debugging section
19534  //print out the full vector here for testing purposes
19535  TTFile3 << "Full LocServiceTimesVector\n\n";
19536  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
19537 
19538  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
19539  {
19540  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
19541  }
19542 
19543  TTFile3 << "\n\n\n";
19544 //end of debugging
19545 */
19546 
19547 /*cdt analysis - added at v2.10.0
19548 2 pass system: 1st extract as a single service all Snt (or Snt-sh) starts, with Fns/Sns links combined (and F-nshs/Sns-sh) (though add a new
19549 changeover code [chr XXXX - 'change ref + new reference] until come to Fjo, Frh, Frh-sh, Fer (ignore exit loc as can't stop there), ignore jbos &
19550 repeats, but with fsp & rsp store all the foregoing service entries along with the split reference & add that to the relevant Sfs entry as a new
19551 service. For shuttles with feeder start with feeder & progress into shuttle, ending when finish & remain here or progressing into the finishing
19552 service.
19553 
19554 Use The TrainDataVectorCopy as that has all unique service refs.
19555 
19556 2nd run the cdt checker similar to that in SecondPassActions, but where a same name found either side of a changeover code quote both refs. Add a
19557 similar unexpected cdt check where if have different locs either side of a cdt then may be inappropriate.
19558 
19559 First create a new TrainDataVector from earlier copy as above with single services
19560 */
19561  if(DirChecked)
19562  {
19563  //direction analysis added at v2.10.0
19564  TTrainDataEntry SingleServiceEntry, PartServiceEntry, NewPartServiceEntry, TempEntry;
19565  TTrainDataVector SingleServiceVector, PartServiceVector;
19566 
19567  //find new train services (Snt or Snt-sh) & remember that entries can be in any order
19568  //NB: ALWAYS use OtherHeadCode (which is now a service reference) for any follow-on service
19569  TTFile3 << "Train direction analysis - consisting of train facing directions on creation and possible missing or questionable changes of direction:\n\n";
19570  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
19571  {
19573  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
19574  {
19575  TDE.ActionVector.erase(TDE.ActionVector.end() - 1); //strip repeat entry if present
19576  }
19577  const TActionVector &AV = TDE.ActionVector;
19578  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
19579  {
19580  SingleServiceEntry = TDE;
19581  TActionVector &SSAV = SingleServiceEntry.ActionVector;
19582  for(unsigned int y = 0; y < SSAV.size(); y++)
19583  {
19584  if((SSAV.at(y).Command == "Fjo") || (SSAV.at(y).Command == "Frh") || (SSAV.at(y).Command == "Fer") || (SSAV.at(y).Command == "Frh-sh"))
19585  {
19586  SingleServiceVector.push_back(SingleServiceEntry); //push the complete entry
19587  break; //finished with this one
19588  }
19589  else if((SSAV.at(y).Command == "fsp") || (SSAV.at(y).Command == "rsp"))
19590  {
19591  PartServiceEntry = TDE; //start with complete entry
19592  PartServiceEntry.ActionVector.clear(); //clear AV
19593  for(unsigned int z = 0; z <= y; z++)
19594  {
19595  PartServiceEntry.ActionVector.push_back(TDE.ActionVector.at(z)); //add back all AVs up to & inc fsp/rsp
19596  if(z == y)
19597  {
19598  PartServiceEntry.ActionVector.at(z).Command = "chr-sp"; //change split command to chr
19599  PartServiceEntry.ActionVector.at(z).OtherHeadCode = PartServiceEntry.ActionVector.at(z).LinkedTrainEntryPtr->ServiceReference;
19600  }
19601  }
19602  PartServiceVector.push_back(PartServiceEntry);
19603  if(SSAV.at(y).Command == "fsp")
19604  {
19605  SSAV.at(y).Command = "Front split - original service continues below";
19606  SSAV.at(y).OtherHeadCode = "";
19607  }
19608  if(SSAV.at(y).Command == "rsp")
19609  {
19610  SSAV.at(y).Command = "Rear split - original service continues below";
19611  SSAV.at(y).OtherHeadCode = "";
19612  }
19613  //don't break & continue here because the original train carries on
19614  }
19615  else if(SSAV.at(y).Command == "Fns")
19616  {
19617  SSAV.at(y).Command = "chr-Fns";
19618  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
19619  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
19620  break; //from y loop
19621  }
19622  else if(SSAV.at(y).Command == "Fns-sh")
19623  {
19624  SSAV.at(y).Command = "chr-Fns-sh";
19625  SSAV.at(y).OtherHeadCode = SSAV.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
19626  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
19627  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
19628  break; //from y loop
19629  }
19630  else if(SSAV.at(y).Command == "F-nshs")
19631  {
19632  SSAV.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
19633  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
19634  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
19635  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
19636  break; //from y loop
19637  }
19638  }
19639  }
19640  }
19641  SequenceLog += "10\n";
19642  //now have all complete entries in SingleServiceVector and all part services in PartServiceVector but without any follow-ons
19643 
19644  //Now add Sns & Sns-sh services to PartServiceVector entries
19645  AnsiString NextRef;
19646  while(!PartServiceVector.empty())
19647  {
19648  PartServiceEntry = PartServiceVector.at(0); //deal with front entry and add new entries at the back
19649  for(unsigned int y = 0; y < PartServiceEntry.ActionVector.size(); y++)
19650  {
19651  if(PartServiceEntry.ActionVector.at(y).Command.SubString(1,3) == "chr")
19652  {
19653  NextRef = PartServiceEntry.ActionVector.at(y).OtherHeadCode;
19654  }
19655  }
19656  //find it in TrainDataVectorCopy
19657  bool FinishType = true, FoundFlag = false;
19658  while(FinishType)
19659  {
19660  TempEntry = GetServiceFromVector(0, NextRef, TrainDataVectorCopy, FinishType, FoundFlag); //FinishType is a bool where false = Final (Fjo, Frh, Fer, or
19661  //Frh-sh); true = MoreToCome (Fns, Fns-sh, F-nshs)
19662  if(FoundFlag)
19663  {
19664  for(unsigned int y = 1; y < TempEntry.ActionVector.size(); y++) //starts at 1 as that is the entry after the start entry
19665  {
19666  if((TempEntry.ActionVector.at(y).Command == "") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
19667  {
19668  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19669  }
19670  else if((TempEntry.ActionVector.at(y).Command[1] != 'F') && (TempEntry.ActionVector.at(y).Command != "fsp") && (TempEntry.ActionVector.at(y).Command != "rsp") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
19671  {
19672  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19673  }
19674  else
19675  {
19676  if((TempEntry.ActionVector.at(y).Command == "Fjo") || (TempEntry.ActionVector.at(y).Command == "Frh") || (TempEntry.ActionVector.at(y).Command == "Fer") || (TempEntry.ActionVector.at(y).Command == "Frh-sh"))
19677  {
19678  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19679  SingleServiceVector.push_back(PartServiceVector.at(0)); //push the complete entry
19680  PartServiceVector.erase(PartServiceVector.begin());
19681  break; //from y loop
19682  }
19683  else if((TempEntry.ActionVector.at(y).Command == "fsp") || (TempEntry.ActionVector.at(y).Command == "rsp"))
19684  {
19685  NewPartServiceEntry = PartServiceVector.at(0); //covers everything up to but excluding the split
19686  NewPartServiceEntry.ActionVector.push_back(TempEntry.ActionVector.at(y)); //now includes the split
19687  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).Command = "chr-sp"; //change split command to chr
19688  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).OtherHeadCode = NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).LinkedTrainEntryPtr->ServiceReference;
19689  PartServiceVector.push_back(NewPartServiceEntry); //new entry for the split service
19690  if(TempEntry.ActionVector.at(y).Command == "fsp")
19691  {
19692  TempEntry.ActionVector.at(y).Command = "Front split - original service continues below";
19693  TempEntry.ActionVector.at(y).OtherHeadCode = "";
19694  }
19695  if(TempEntry.ActionVector.at(y).Command == "rsp")
19696  {
19697  TempEntry.ActionVector.at(y).Command = "Rear split - original service continues below";
19698  TempEntry.ActionVector.at(y).OtherHeadCode = "";
19699  }
19700  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19701  }
19702  else if(TempEntry.ActionVector.at(y).Command == "Fns")
19703  {
19704  TempEntry.ActionVector.at(y).Command = "chr-Fns";
19705  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
19706  TempEntry.ActionVector.at(y).OtherHeadCode = NextRef;
19707  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
19708  break; //from y loop
19709  }
19710  else if(TempEntry.ActionVector.at(y).Command == "Fns-sh")
19711  {
19712  TempEntry.ActionVector.at(y).Command = "chr-Fns-sh";
19713  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
19714  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
19715  NextRef = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
19716  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
19717  break; //from y loop
19718  }
19719  else if(TempEntry.ActionVector.at(y).Command == "F-nshs")
19720  {
19721  TempEntry.ActionVector.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
19722  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
19723  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
19724  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
19725  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
19726  break; //from y loop
19727  }
19728  }
19729  }
19730  }
19731  else
19732  {
19733  SequenceLog += + "11\n";
19734  throw Exception("Unable to find service reference " + NextRef + " Last ref checked = " + TempEntry.ServiceReference);
19735  }
19736  }
19737  }
19738  if(!PartServiceVector.empty())
19739  {
19740  SequenceLog += "12\n";
19741  throw Exception("PartServiceVector should be empty here - size = " + AnsiString(PartServiceVector.size()));
19742  }
19743  SequenceLog += "13\n";
19744  /*
19745  form:-
19746  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
19747  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
19748  then multiple entries, separated by commas, of the form:-
19749 
19750  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
19751  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
19752  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
19753 
19754  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
19755  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
19756  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
19757 
19758  HH:MM;Command (cdt) }TimeCmd }
19759  HH:MM;Command;new description (dsc) }TimeCmdDescription }
19760  HH:MM;Location (arr & dep) }TimeLoc }
19761  HH:MM;HH:MM;Location }TimeTimeLoc }
19762  HH:MM;pas;Location }PassTime }
19763  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
19764  HH:MM;Fer;set of allowable IDs }ExitRailway }
19765  Command (Frh only) }FinRemHere }
19766 
19767  R;mm;dd;nn. Repeat Repeat entry
19768 
19769  Formats:
19770 
19771  Command only: Frh
19772  Time;Command: cdt
19773  Time;Command;new description: dsc
19774  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
19775  Time;Command;2 Element IDs: Snt
19776  Time;Comand;n Element IDs: Fer
19777  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
19778  Time;Command;2 Element IDs;Headcode Snt-sh
19779  Time;Command;Location pas
19780  Time;Location Arr Dep
19781  Time;Time;Location Arr & dep together
19782  */
19783 
19784 /*
19785 Perform the starting direction check (Snt & Snt-sh entries). Starts with the train's front element,
19786 checking forwards until it comes to a continuation (no report), a location name that is not null and
19787 different to the train's front element name (whether null or not) (no report), a leading point
19788 (no report) or buffers (report).
19789 */
19790  bool BufferFacingUnReportedFlag = true;
19791  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
19792  {
19793  TTrackElement ThisElement, NextElement;
19794  TTrainDataEntry TDE = SingleServiceVector.at(x);
19795  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
19796  {
19797  SequenceLog += "13a\n";
19798  throw Exception("Repeat entry present in SingleServiceVector at position " + AnsiString(x));
19799  }
19800  const TActionVector &AV = TDE.ActionVector;
19801  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
19802  {
19803  bool BufferFlag = false;
19804  int FrontTVPos = AV.at(0).FrontStartOrRepeatDigits;
19805  int RearTVPos = AV.at(0).RearStartOrRepeatMins;
19806  AnsiString FrontLocName = AV.at(0).LocationName;
19807  int NextEntryPos, NextExitPos;
19808  ThisElement = Track->TrackElementAt(1395, FrontTVPos);
19809  int ThisExitPos;
19810  if(ThisElement.Conn[0] == RearTVPos)
19811  {
19812  ThisExitPos = 1;
19813  }
19814  else if(ThisElement.Conn[1] == RearTVPos)
19815  {
19816  ThisExitPos = 0;
19817  }
19818  else if(ThisElement.Conn[2] == RearTVPos)
19819  {
19820  ThisExitPos = 3;
19821  }
19822  else if(ThisElement.Conn[3] == RearTVPos)
19823  {
19824  ThisExitPos = 2;
19825  }
19826  if((ThisElement.TrackType == Buffers) && (ThisExitPos == 0))//pos 0 is the buffer
19827  {
19828  BufferFlag = true;
19829  }
19830  else //continue tracking forwards
19831  {
19832  while(true)
19833  {
19834  if(ThisElement.Conn[ThisExitPos] == -1)
19835  {
19836  SequenceLog = "13b\n";
19837  throw Exception("ThisElement connects to -1 for " + TDE.ServiceReference);
19838  }
19839  NextElement = Track->TrackElementAt(1396, ThisElement.Conn[ThisExitPos]);
19840  NextEntryPos = ThisElement.ConnLinkPos[ThisExitPos];
19841  if((NextElement.TrackType == Points) && ((NextEntryPos == 0) || (NextEntryPos == 2))) //leading points
19842  {
19843  BufferFlag = false; //should already be false
19844  break;
19845  }
19846  else if(NextElement.TrackType == Continuation)
19847  {
19848  BufferFlag = false;
19849  break;
19850  }
19851  else if((NextElement.ActiveTrackElementName != "") && (NextElement.ActiveTrackElementName != FrontLocName))
19852  {
19853  BufferFlag = false;
19854  break;
19855  }
19856  else if(NextElement.TrackType == Buffers)
19857  {
19858  BufferFlag = true;
19859  break;
19860  }
19861  else if((NextElement.TrackType == Points) && ((NextEntryPos == 1) || (NextEntryPos == 3))) //trailing points
19862  {
19863  ThisElement = NextElement;
19864  ThisExitPos = 0;
19865  continue;
19866  }
19867  else
19868  {
19869  if(NextEntryPos == 0)
19870  {
19871  NextExitPos = 1;
19872  }
19873  else if(NextEntryPos == 1)
19874  {
19875  NextExitPos = 0;
19876  }
19877  else if(NextEntryPos == 2)
19878  {
19879  NextExitPos = 3;
19880  }
19881  else if(NextEntryPos == 3)
19882  {
19883  NextExitPos = 2;
19884  }
19885  }
19886  ThisElement = NextElement;
19887  ThisExitPos = NextExitPos;
19888  }
19889  }
19890  if(BufferFlag)
19891  {
19892  if(BufferFacingUnReportedFlag)
19893  {
19894  TTFile3 << "Train facing direction on creation analysis:-\n\n";
19895  BufferFacingUnReportedFlag = false;
19896  }
19897  TTFile3 << "Service " + TDE.ServiceReference + " facing buffers on creation\n";
19898  }
19899  }
19900  }
19901  if(BufferFacingUnReportedFlag)
19902  {
19903  TTFile3 << "Nothing to report for train facing directions\n\n";
19904  }
19905  else
19906  {
19907  TTFile3 << '\n';
19908  }
19909  SequenceLog += "13c\n";
19910 
19911  //Perform the missing cdt check. Check every entry simiar to the check in SecondPassActions and if find any print out the full sequence and service entries
19912  AnsiString LocationNameToBeChecked = "";
19913  bool MissingcdtUnreportedFlag = true;
19914  TNumList MarkerList;
19915  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
19916  {
19917  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
19918  unsigned int y = 0;
19919  int FirstInstance = 9999, SecondInstance = 9999; //9999 ensures won't be marked if not changed
19920  bool FullBreak = false;
19921  MarkerList.clear();
19922  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
19923  // first discard unlocated Snt entries as they don't have location name set
19924  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
19925  {
19926  y = 1;
19927  }
19928  while((y < TDEntry.ActionVector.size()) && !FullBreak)
19929  // need to check each location name separately in turn, skipped for SignallerControl entries
19930  {
19931  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat) ||
19932  (TDEntry.ActionVector.at(y).Command == "Fjo") || (TDEntry.ActionVector.at(y).Command == "Frh") ||
19933  (TDEntry.ActionVector.at(y).Command == "Frh-sh"))
19934  {
19935  break; // out of the 'while' loop since have reached the end
19936  }
19937  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
19938  FirstInstance = y;
19939  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
19940  {
19941  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
19942  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) ||
19943  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
19944  (AVEntry.Command == "Frh-sh"))
19945  {
19946  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
19947  }
19948  if(AVEntry.Command == "cdt")
19949  {
19950  break; // out of the 'z' loop since the check is only valid up to a change of direction
19951  }
19952  if(AVEntry.LocationName == LocationNameToBeChecked)
19953  {
19954  continue; // keep going while name same
19955  }
19956  if(AVEntry.LocationName != LocationNameToBeChecked)
19957  // if name different check forwards to see if repeats
19958  {
19959  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
19960  {
19961  if(TDEntry.ActionVector.at(a).Command == "cdt")
19962  {
19963  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
19964  }
19965  if(TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked)
19966  {
19967  SecondInstance = a;
19968  AnsiString Sequence = TDEntry.ServiceReference;
19969  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
19970  {
19971  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
19972  {
19973  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
19974  }
19975  }
19976  if(MissingcdtUnreportedFlag)
19977  {
19978  TTFile3 << "Possibly missing changes of direction - these will be missing unless the service travels in a loop back to the locations marked:-\n\n";
19979  }
19980  TTFile3 << LocationNameToBeChecked << " is listed twice with no direction change between in service sequence: " << Sequence << "\n\n";
19981  MarkerList.push_back(FirstInstance);
19982  MarkerList.push_back(SecondInstance);
19983  SingleServiceOutput(0, x, MarkerList, SingleServiceVector, TTFile3);
19984  MissingcdtUnreportedFlag = false;
19985  FullBreak = true; //no more checks for this sequence
19986  break; //out of the a & z loops
19987  }
19988  }
19989  break; // out of the 'z' loop since have checked 'a' as far as need to
19990  }
19991  }
19992  y++;
19993  }
19994  }
19995  if(MissingcdtUnreportedFlag)
19996  {
19997  TTFile3 << "Nothing to report for missing changes of direction\n\n";
19998  }
19999  else
20000  {
20001  TTFile3 << '\n';
20002  }
20003  SequenceLog += "14\n";
20004 
20005 /* Perform the questionable cdt check. Examine each service in the SingleServiceVector, and if don't find the same
20006  name either side of a cdt (before the next cdt) then flag as a questionable cdt.
20007  Method: Have an outer loop for each service that looks for cdts. When found work backwards to the last cdt or beginning and std::list all the
20008  locations excluding the cdt location. Then work forwards to the next cdt or the end and do the same. Sort each list and make unique (duplicated
20009  names on one side of a cdt already checked either by the tt validator or the missing cdt check. Then compare the two lists and if any location
20010  included in both then ok, else report as questionable. If one list is empty then it is reported.
20011 */
20012  typedef std::list<AnsiString> TLocList;
20013  TLocList BackwardList, ForwardList;
20014  bool IntroLineNeeded = true;
20015  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
20016  {
20017  unsigned int cdtPosition = 9999;
20018  AnsiString cdtLocation = "";
20019  bool FoundSameName = false;
20020  MarkerList.clear();
20021  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
20022  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
20023  // need to check each location name separately in turn, skipped for SignallerControl entries
20024  {
20025  BackwardList.clear();
20026  ForwardList.clear();
20027  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
20028  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) || //end of SSVector, shouldn't be any repeats
20029  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
20030  (AVEntry.Command == "Frh-sh"))
20031  {
20032  if(MarkerList.empty())
20033  {
20034  break; // out of the 'y' loop since have reached the end & nothing to report
20035  }
20036  else
20037  {
20038  AnsiString Sequence = TDEntry.ServiceReference;
20039  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
20040  {
20041  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
20042  {
20043  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
20044  }
20045  }
20046  MarkerList.sort();
20047  if(IntroLineNeeded)
20048  {
20049  TTFile3 << "Questionable change of direction analysis.\n\n";
20050  TTFile3 << "For marked changes of direction there are no same-name locations listed both above (up to the start or another direction change)\n";
20051  TTFile3 << "and below (down to the end or another direction change) but not counting the change of direction location itself.\n\n";
20052  TTFile3 << "These changes of direction are probably valid for movements to and from depots but all should be checked to\n";
20053  TTFile3 << "make sure that none has been included incorrectly:\n\n";
20054  IntroLineNeeded = false;
20055  }
20056  TTFile3 << "Service sequence " << Sequence << " contains questionable changes of direction:-\n\n";
20057  SingleServiceOutput(1, x, MarkerList, SingleServiceVector, TTFile3);
20058  break;
20059  }
20060  }
20061  if(AVEntry.Command != "cdt")
20062  {
20063  continue; //only looking for cdts
20064  }
20065  //here have found a cdt
20066  cdtPosition = y;
20067  cdtLocation = AVEntry.LocationName;
20068  for(int z = y - 1; z >= 0; z--)
20069  {
20070  const TActionVectorEntry &AVEntry2 = TDEntry.ActionVector.at(z);
20071  if(AVEntry2.Command == "cdt")
20072  {
20073  break; //don't look further back than the last cdt
20074  }
20075  if((AVEntry2.LocationName != "") && (AVEntry2.LocationName != cdtLocation)) //if an earlier entry == cdtLocation will have been picked up in an earlier check
20076  {
20077  BackwardList.push_back(AVEntry2.LocationName);
20078  }
20079  }
20080  BackwardList.sort();
20081  BackwardList.unique();
20082  for(unsigned int z = y + 1; z < TDEntry.ActionVector.size(); z++)
20083  {
20084  const TActionVectorEntry &AVEntry3 = TDEntry.ActionVector.at(z);
20085  if((AVEntry3.Command == "Fer") || (AVEntry3.FormatType == Repeat) ||
20086  (AVEntry3.Command == "Fjo") || (AVEntry3.Command == "Frh") ||
20087  (AVEntry3.Command == "Frh-sh") || (AVEntry3.Command == "cdt"))
20088  {
20089  break; // out of the 'z' loop since have reached another cdt or the end
20090  }
20091  if((AVEntry3.LocationName != "") && (AVEntry3.LocationName != cdtLocation)) //if a later entry == cdtLocation will have been picked up in an earlier check
20092  {
20093  ForwardList.push_back(AVEntry3.LocationName);
20094  }
20095  }
20096  ForwardList.sort();
20097  ForwardList.unique();
20098  FoundSameName = false;
20099  //now have both lists compiled (may be empty) so check for same name in both & report if don't find any
20100  if(!BackwardList.empty() && !ForwardList.empty())
20101  {
20102  for(TLocList::iterator BLIt = BackwardList.begin(); BLIt != BackwardList.end(); BLIt++)
20103  {
20104  for(TLocList::iterator FLIt = ForwardList.begin(); FLIt != ForwardList.end(); FLIt++)
20105  {
20106  if(*BLIt == *FLIt)
20107  {
20108  FoundSameName = true;
20109  }
20110  }
20111  }
20112  }
20113  if(!FoundSameName) //report the inability to find same name
20114  {
20115  MarkerList.push_back(cdtPosition);
20116  }
20117  }
20118  }
20119  if(IntroLineNeeded)
20120  {
20121  TTFile3 << "Nothing to report for questionable changes of direction\n\n";
20122  }
20123  else
20124  {
20125  TTFile3 << '\n';
20126  }
20127 /*
20128 //debug section
20129 //print all SSVector for diagnostic purposes
20130  TTFile3 << "Whole SSVector\n\n";
20131  TNumList EmptyList;
20132  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
20133  {
20134  SingleServiceOutput(, x, EmptyList, SingleServiceVector, TTFile3);
20135  }
20136 //end of debug section
20137 */
20138  }
20139  SequenceLog += "15\n";
20140  TTFile3.close();
20141  Utilities->CallLogPop(2212);
20142  return(true);
20143  }
20144 
20145  catch(const Exception &e) //non error catch
20146  {
20147  AnsiString TTErrorFileName = "Analysis Error.txt";
20148  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
20149  std::ofstream TTError(TTErrorFileName.c_str());
20150  if(TTError == 0)
20151  {
20152  ShowMessage("Analysis error file failed to open - can't be created");
20153  Utilities->CallLogPop(2233);
20154  return(false);
20155  }
20156  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
20157  TTError << TimeNow.c_str() << '\n' << ArrRange << '\n' << ArrChecked << '\n' << DepRange << '\n' <<
20158  DepChecked << '\n' << AtLocChecked << '\n' << SequenceLog << '\n' << AnsiString(e.Message);
20159 
20160  TTError.close();
20161  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
20162  Utilities->CallLogPop(2226);
20163  return(false);
20164  }
20165 }
20166 
20167 // ---------------------------------------------------------------------------
20168 void TTrainController::SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
20169 {
20170  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(SSVectorNumber) + ',' + ",SingleServiceOutput");
20171  if((SSVectorNumber < 0) || ((unsigned int)SSVectorNumber >= SingleServiceVector.size()))
20172  {
20173  throw Exception("SSVectorNumber out of range, = " + AnsiString(SSVectorNumber) + ", Vector size = " + SingleServiceVector.size());
20174  }
20175  TTrainDataEntry SingleService = SingleServiceVector.at(SSVectorNumber);
20176  {
20177  VecFile << ",Initial service reference " << SingleService.ServiceReference + '\n';
20178  AnsiString Marker = "";
20179  for(unsigned int x = 0; x < SingleService.ActionVector.size(); x++)
20180  {
20181  Marker = ',';
20182  for(TNumListIterator MLIt = MarkerList.begin(); MLIt != MarkerList.end(); MLIt++)
20183  {
20184  if(int(x) == *MLIt)
20185  {
20186  Marker = "-->,";
20187  break;
20188  }
20189  }
20190  TActionVectorEntry AVE = SingleService.ActionVector.at(x);
20191  if(AVE.FormatType == StartNew)
20192  {
20193  AnsiString RearID = Track->TrackElementAt(1397, AVE.RearStartOrRepeatMins).ElementID;
20194  AnsiString FrontID = Track->TrackElementAt(1398, AVE.FrontStartOrRepeatDigits).ElementID;
20195  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << '\n';
20196  }
20197  if(AVE.FormatType == SNTShuttle)
20198  {
20199  AnsiString RearID = Track->TrackElementAt(1399, AVE.RearStartOrRepeatMins).ElementID;
20200  AnsiString FrontID = Track->TrackElementAt(1400, AVE.FrontStartOrRepeatDigits).ElementID;
20201  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << ' ' << AVE.OtherHeadCode << '\n';
20202  }
20203  if(AVE.FormatType == SNSShuttle) //should all have been converted to chr
20204  {
20205  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
20206  }
20207  if(AVE.FormatType == Repeat) //shouldn't be any repeats, only here to show if any have been copied
20208  {
20209  VecFile << Marker << "Repeat " << AVE.RearStartOrRepeatMins << ' ' << AVE.FrontStartOrRepeatDigits << ' ' << AVE.NumberOfRepeats << '\n';
20210  }
20211  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
20212  {
20213  TActionVectorEntry AVHolder = AVE;
20214  if(AVE.Command.SubString(1,3) == "chr")
20215  {
20216  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "sp")
20217  {
20218  AVE.Command = "Change of service to " + AVE.OtherHeadCode + " after split";
20219  AVE.OtherHeadCode = "";
20220  }
20221  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns")
20222  {
20223  AVE.Command = "Change of service to ";
20224  }
20225  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns-sh")
20226  {
20227  AVE.Command = "Change to shuttle finishing service";
20228  }
20229  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "F-nshs")
20230  {
20231  AVE.Command = "Change to shuttle service " + AVE.OtherHeadCode + " from feeder";
20232  AVE.OtherHeadCode = "";
20233  }
20234  }
20235  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << '\n';
20236  AVE = AVHolder;
20237  }
20238  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
20239  {
20240  VecFile << Marker << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
20241  }
20242  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
20243  {
20244  VecFile << Marker << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
20245  }
20246  else if(AVE.FormatType == TimeTimeLoc)
20247  {
20248  VecFile << Marker << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
20249  }
20250  else if(AVE.FormatType == PassTime)
20251  {
20252  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
20253  }
20254  else if(AVE.FormatType == ExitRailway) //ListOfExits added at v2.10.0
20255  {
20256  AnsiString ListOfExits = "";
20257  for(TNumListIterator NLIt = AVE.ExitList.begin(); NLIt != AVE.ExitList.end(); NLIt++)
20258  {
20259  ListOfExits += AnsiString(Track->TrackElementAt(1432, *NLIt).ElementID) + ' ';
20260  }
20261  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << " Fer " << ListOfExits <<'\n';
20262  }
20263  else if(AVE.FormatType == FinRemHere)
20264  {
20265  VecFile << Marker << "Frh" << '\n';
20266  }
20267  }
20268  VecFile << '\n';
20269  }
20270  Utilities->CallLogPop(2318);
20271 }
20272 
20273 // ---------------------------------------------------------------------------
20274 
20275 TTrainDataEntry TTrainController::GetServiceFromVector(AnsiString Caller, AnsiString HeadCode, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
20276 {
20277  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetServiceFromVector," + HeadCode);
20278  FoundFlag = false;
20279  FinishType = true;
20280  for(unsigned int x = 0; x < Vector.size(); x++)
20281  {
20282 // AnsiString ThisRef = Vector.at(x).ServiceReference;
20283  if(Vector.at(x).ServiceReference == HeadCode)
20284  {
20285  if(Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1).FormatType == Repeat) //shouldn't be any repeats
20286  {
20287  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 2);
20288  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
20289  {
20290  FinishType = false;
20291  }
20292  }
20293  else
20294  {
20295  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1);
20296  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
20297  {
20298  FinishType = false;
20299  }
20300  }
20301  FoundFlag = true;
20302  Utilities->CallLogPop(2319);
20303  return(Vector.at(x));
20304  }
20305  }
20306  Utilities->CallLogPop(2320);
20307  return(Vector.at(Vector.size() - 1)); //return last for want of returning something
20308 }
20309 
20310 // ---------------------------------------------------------------------------
20311 
20312 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
20313 {
20314 //convert times to integer minutes
20315  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
20316  if((Time1 == "") || (Time2 == ""))
20317  {
20318  Utilities->CallLogPop(2213);
20319  return(false);
20320  }
20321  int Mins = Time1.SubString(4,2).ToInt();
20322  int Hours = Time1.SubString(1,2).ToInt();
20323  int Time1Mins = (Hours * 60) + Mins;
20324  Mins = Time2.SubString(4,2).ToInt();
20325  Hours = Time2.SubString(1,2).ToInt();
20326  int Time2Mins = (Hours * 60) + Mins;
20327  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
20328  {
20329  Utilities->CallLogPop(2214);
20330  return(true);
20331  }
20332  Utilities->CallLogPop(2215);
20333  return(false);
20334 }
20335 
20336 // ---------------------------------------------------------------------------
20337 
20338 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
20339  bool &AnalysisError, int &MaxNumberOfSameDirections)
20340 {
20341  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
20342 
20343  try
20344  {
20345  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
20346  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
20347  int SCPos = 0;
20348  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
20349  //first change every second comma in Input to a semicolon so can separate services but keep times with services
20350  bool EvenComma = false;
20351  for(int x = 1; x <= Input.Length(); x++)
20352  {
20353  TempStr1 = Input[x];
20354  if(TempStr1 == AnsiString(',') && EvenComma)
20355  {
20356  TempStr2 += ';';
20357  }
20358  else
20359  {
20360  TempStr2 += Input[x];
20361  }
20362  if(TempStr1 == AnsiString(','))
20363  {
20364  EvenComma = !EvenComma;
20365  }
20366  }
20367  //load up the list of services with associated times
20368  while(TempStr2.Length() > 0)
20369  {
20370  SCPos = TempStr2.Pos(';');
20371  if(SCPos > 0) //0 if not found, as won't be when only one service left
20372  {
20373  OneService = TempStr2.SubString(1, SCPos - 1);
20374  ServiceList.push_back(OneService);
20375  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
20376  }
20377  else //no semicolon so looking at last (or only) element
20378  {
20379  ServiceList.push_back(TempStr2);
20380  TempStr2 = "";
20381  }
20382  }
20383  ServiceList.sort(); // alphabetical order
20384  ServiceList.unique(); //remove duplicates
20385  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
20386 
20387  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
20388  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
20389  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
20390  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
20391 
20392  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20393  {
20394  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
20395  }
20396  SLIt3 = ServiceList.end();
20397  SLIt3--; //so points to last element
20398  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
20399  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
20400  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
20401  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
20402  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
20403  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
20404 
20405  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
20406  {
20407  SLIt = SLIt1;
20408  SLIt++; //so points to one after SLIt1
20409  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
20410  {
20411  continue; //already allocated so skip to the next
20412  }
20413  else
20414  {
20415  CommaPos1 = SLIt1->Pos(','); //can't be 0
20416  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
20417  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
20418  SpacePos = ServiceRef1.Pos(' ');
20419  RepeatNum1 = 0;
20420  if(SpacePos > 0) //otherwise it's already correct
20421  {
20422  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
20423  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
20424  if(RepeatInfo1[1] == 'F')
20425  {
20426  RepeatNum1 = 0;
20427  }
20428  else
20429  {
20430  SpacePos = RepeatInfo1.Pos(' ');
20431  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
20432  }
20433  }
20434  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
20435  //but this includes the "&0" etc so need to strip these
20436  AmpersandPos = AnsiTime1.Pos('&');
20437  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
20438 
20439  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
20440  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
20441  {
20442  throw Exception("ASCLIt1 Error in " + Input);
20443  }
20444  ServiceCallingLocsList1 = ASCLIt1->second;
20445  AmpersandPos = SLIt1->Pos('&');
20446  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
20447  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
20448 
20449  SameDirectionCount = 1;
20450  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
20451  {
20452  CommaPos2 = SLIt2->Pos(','); //can't be 0
20453  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
20454  //but this contains "(First service..." etc so need to strip these
20455  SpacePos = ServiceRef2.Pos(' ');
20456  RepeatNum2 = 0;
20457  if(SpacePos > 0) //otherwise it's already correct
20458  {
20459  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
20460  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
20461  if(RepeatInfo2[1] == 'F')
20462  {
20463  RepeatNum2 = 0;
20464  }
20465  else
20466  {
20467  SpacePos = RepeatInfo2.Pos(' ');
20468  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
20469  }
20470  }
20471  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
20472  //but this includes the "&0" etc so need to strip these
20473  AmpersandPos = AnsiTime2.Pos('&');
20474  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
20475 
20476  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
20477  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
20478  {
20479  throw Exception("ASCLIt2 Error in " + Input);
20480  }
20481  ServiceCallingLocsList2 = ASCLIt2->second;
20482  //now compare the two
20483  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
20484  {
20485  int AmpersandPos = SLIt2->Pos('&');
20486  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
20487  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
20488  SameDirectionCount++;
20489  }
20490  }
20491  if(SameDirectionCount > MaxNumberOfSameDirections)
20492  {
20493  MaxNumberOfSameDirections = SameDirectionCount;
20494  }
20495  }
20496  }
20497 
20498  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
20499  {
20500  //any existing direction so allocate it now
20501  AmpersandPos = SLIt3->Pos('&');
20502  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
20503  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
20504  }
20505  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
20506  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20507  {
20508  //extract the DirectionMarker as an integer
20509  AmpersandPos = SLIt->Pos('&');
20510  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
20511  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
20512  DirectionMarker = DirectionMarkerString.ToInt();
20513  AnsiString DirectionSuffix = "";
20514  char c;
20515  if(DirectionMarker < 27)
20516  {
20517  c = 64 + DirectionMarker; //so 1 -> 'A'
20518  DirectionSuffix = "," + AnsiString(c);
20519  }
20520  else if(DirectionMarker < 53)
20521  {
20522  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
20523  DirectionSuffix = ",A" + AnsiString(c);
20524  }
20525  else
20526  {
20527  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
20528  }
20529  *SLIt = ServiceWithoutMarker + DirectionSuffix;
20530  }
20531  //now prepare the final consolidated output
20532  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20533  {
20534  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
20535  }
20536  if(Output.Length() > 0)
20537  {
20538  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
20539  }
20540  Utilities->CallLogPop(2216);
20541  return(Output);
20542  }
20543 
20544  catch(const Exception &e) //non error catch
20545  {
20546  AnalysisError = true;
20547  Utilities->CallLogPop(2227);
20548  return(e.Message);
20549  }
20550 }
20551 
20552 // ---------------------------------------------------------------------------
20553 
20554 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
20555 {
20556  //similar to above but doesn't include times in the input
20557  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
20558  AnsiString InternalInput = Input, Output = "", OneService = "";
20559  int CommaPos = 0;
20560  std::list<AnsiString> ServiceList;
20561  //load up the list
20562  while(InternalInput.Length() > 0)
20563  {
20564  CommaPos = InternalInput.Pos(',');
20565  if(CommaPos > 0) //0 if not found, as won't be when only one service left
20566  {
20567  OneService = InternalInput.SubString(1, CommaPos - 1);
20568  ServiceList.push_back(OneService);
20569  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
20570  }
20571  else //no comma so looking at last (or only) element
20572  {
20573  ServiceList.push_back(InternalInput);
20574  InternalInput = "";
20575  }
20576  }
20577 
20578  ServiceList.sort(); // alphabetical order
20579  ServiceList.unique(); //remove duplicates
20580  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
20581  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20582  {
20583  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
20584  }
20585  if(Output.Length() > 0)
20586  {
20587  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
20588  }
20589  Utilities->CallLogPop(2217);
20590  return(Output);
20591 }
20592 
20593 // ---------------------------------------------------------------------------
20594 
20595 
20596 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
20597  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
20598 {
20599  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
20600  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
20601 
20602  std::list<AnsiString>::iterator LP1, LP2, ListPtr1, ListPtr2, LocPtr1, LocPtr2; //LP1 & 2 are temporary pointers, ListPtrs are
20603  //general list pointers, LocPtrs point to Location in the two lists
20604 
20605  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
20606  //for List1
20607  bool LocFound = false;
20608  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
20609  int IncMinutes;
20610  TDateTime FirstServiceTime;
20611 
20612  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
20613  int Ref1Target = 0, Ref1Count = 0;
20614  int Ref2Target = 0, Ref2Count = 0;
20615 
20616 /* drop this after retained slashes in ServiceRef
20617  int SlashPos = Ref1.Pos('/');
20618  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
20619  {
20620  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
20621  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
20622  }
20623  int Ref2Target = 0, Ref2Count = 0;
20624  SlashPos = Ref2.Pos('/');
20625  if(SlashPos > 0) //if 0 leave as is
20626  {
20627  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
20628  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
20629  }
20630 */
20631 
20632  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
20633  {
20634  //even if others have same names. But if there are cdt's then need to refind the correct service
20635  if((*ListPtr1) == Location) //
20636  {
20637  LocPtr1 = ListPtr1; //may be modified later
20638  LocFound = true;
20639  }
20640  if(ListPtr1->SubString(1, 3) == "%%%")
20641  {
20642  AnsiString CDTTime = ListPtr1->SubString(4, 5);
20643  //now adjust the time to correspond to the repeat if there is one
20644  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
20645  {
20646  IncMinutes = -1;
20647  FirstServiceTime = TDateTime(-1);
20648  bool BreakFlag = false;
20649  for(TTrainDataVector::iterator TDVIt = TrainDataVectorCopy.begin(); TDVIt != TrainDataVectorCopy.end(); TDVIt++)
20650  {
20651  if(TDVIt->ServiceReference == Ref1)
20652  {
20653  if(Ref1Target > Ref1Count)
20654  {
20655  Ref1Count++;
20656  continue;
20657  }
20658  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
20659  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
20660  {
20661  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
20662  {
20663  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
20664  BreakFlag = true;
20665  break;
20666  }
20667  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
20668  {
20669  FirstServiceTime = AVIt->ArrivalTime;
20670  BreakFlag = true;
20671  break;
20672  }
20673  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
20674  {
20675  FirstServiceTime = AVIt->DepartureTime;
20676  BreakFlag = true;
20677  break;
20678  }
20679  }
20680  if(BreakFlag)
20681  {
20682  break;
20683  }
20684  }
20685  }
20686  if(IncMinutes == -1)
20687  {
20688  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20689  }
20690  if(FirstServiceTime == TDateTime(-1))
20691  {
20692  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20693  }
20694  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
20695  }
20696  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
20697  {
20698  LocFound = false;
20699  continue;
20700  }
20701  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
20702  {
20703  break;
20704  }
20705  if(Time1 > CDTTime) //not there yet so go on
20706  {
20707  LocFound = false;
20708  continue;
20709  }
20710  if(Time1 < CDTTime) //gone too far so can stop now
20711  {
20712  break;
20713  }
20714  }
20715  }
20716  if(!LocFound) //have to find it in both lists
20717  {
20718  Utilities->CallLogPop(2228);
20719  return( false);
20720  }
20721  //for List2
20722  LocFound = false;
20723  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
20724  {
20725  if((*ListPtr2) == Location)
20726  {
20727  LocPtr2 = ListPtr2; //may be modified later
20728  LocFound = true;
20729  }
20730  if(ListPtr2->SubString(1, 3) == "%%%")
20731  {
20732  AnsiString CDTTime = ListPtr2->SubString(4, 5);
20733  //now adjust the time to correspond to the repeat if there is one
20734  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
20735  {
20736  IncMinutes = -1;
20737  FirstServiceTime = TDateTime(-1);
20738  bool BreakFlag = false;
20739  for(TTrainDataVector::iterator TDVIt = TrainDataVectorCopy.begin(); TDVIt != TrainDataVectorCopy.end(); TDVIt++)
20740  {
20741  if(TDVIt->ServiceReference == Ref2)
20742  {
20743  if(Ref2Target > Ref2Count)
20744  {
20745  Ref2Count++;
20746  continue;
20747  }
20748  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
20749  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
20750  {
20751  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
20752  {
20753  FirstServiceTime = AVIt->EventTime;
20754  BreakFlag = true;
20755  break;
20756  }
20757  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
20758  {
20759  FirstServiceTime = AVIt->ArrivalTime;
20760  BreakFlag = true;
20761  break;
20762  }
20763  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
20764  {
20765  FirstServiceTime = AVIt->DepartureTime;
20766  BreakFlag = true;
20767  break;
20768  }
20769  }
20770  if(BreakFlag)
20771  {
20772  break;
20773  }
20774  }
20775  }
20776  if(IncMinutes == -1)
20777  {
20778  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20779  }
20780  if(FirstServiceTime == TDateTime(-1))
20781  {
20782  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20783  }
20784  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
20785  }
20786  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
20787  {
20788  LocFound = false;
20789  continue;
20790  }
20791  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
20792  {
20793  break;
20794  }
20795  if(Time2 > CDTTime) //not there yet so go on
20796  {
20797  LocFound = false;
20798  continue;
20799  }
20800  if(Time2 < CDTTime) //gone too far so can stop now
20801  {
20802  break;
20803  }
20804  }
20805  }
20806  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
20807  {
20808  Utilities->CallLogPop(2229);
20809  return( false);
20810  }
20811  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
20812  //set ListPtr1 to the search start position
20813  if(Arrival)
20814  {
20815  LP1 = List1.begin();
20816  LP1--; //now points to before the first entry
20817  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
20818  {
20819  if(ListPtr1 == List1.begin())
20820  {
20821  break;
20822  }
20823  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
20824  {
20825  ListPtr1++; //point to one past the cdt
20826  break;
20827  }
20828  }
20829  //set ListPtr2 to the search start position
20830  LP2 = List2.begin();
20831  LP2--; //now points to before the first entry
20832  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
20833  {
20834  if(ListPtr2 == List2.begin())
20835  {
20836  break;
20837  }
20838  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
20839  {
20840  ListPtr2++; //point to one past the cdt
20841  break;
20842  }
20843  }
20844  //ListPtr1 & 2 now at search start position
20845  LP1 = ListPtr1;
20846  LP2 = ListPtr2;
20847  //now search forwards, i.e. for common locations before Location
20848  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
20849  {
20850  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
20851  {
20852  break;
20853  }
20854  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
20855  {
20856  break;
20857  }
20858  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
20859  {
20860  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
20861  {
20862  break;
20863  }
20864  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
20865  {
20866  break;
20867  }
20868  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
20869  {
20870  Utilities->CallLogPop(2230);
20871  return( true);
20872  }
20873  }
20874  }
20875  }
20876 
20877  //now, for the departure analysis, reset the start positions and search locations after Location
20878 
20879  else
20880  {
20881  LP1 = LocPtr1;
20882  LP1++; //start at one past the location itself
20883  LP2 = LocPtr2;
20884  LP2++;
20885  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
20886  {
20887  if(ListPtr1 == List1.end()) //reached end point so stop
20888  {
20889  break;
20890  }
20891  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
20892  {
20893  break;
20894  }
20895  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
20896  {
20897  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
20898  {
20899  break;
20900  }
20901  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
20902  {
20903  break;
20904  }
20905  if((*ListPtr1) == (*ListPtr2)) //found a common later location
20906  {
20907  Utilities->CallLogPop(2231);
20908  return( true);
20909  }
20910  }
20911  }
20912  }
20913  Utilities->CallLogPop(2232);
20914  return( false);
20915 }
20916 
20917 // ---------------------------------------------------------------------------
20918 
20919 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
20920 {
20921  // changed at v2.7.0 to show allowable exit elements
20922  if(ExitList.empty())
20923  {
20924  return("");
20925  }
20926  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
20927  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
20928  AnsiString ExitLocList = "";
20929  AllowedExits = "";
20930 
20931  unsigned int Counter = 0;
20932  for(TNumListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
20933  {
20934  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
20935  Counter++;
20936  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
20937  {
20938  ExitLocList += "\n";
20939  }
20940  }
20941  if(StartName == "")
20942  {
20943  if(ExitList.size() == 1)
20944  {
20945  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
20946  Utilities->CallLogPop(1571);
20947  return(" at " + ID);
20948  }
20949  else
20950  {
20951  Utilities->CallLogPop(1572);
20952  if(ExitList.size() < 4)
20953  {
20954  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
20955  return("");
20956  }
20957  else
20958  {
20959  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
20960  return("");
20961  }
20962  }
20963  }
20964  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
20965  {
20966  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
20967  {
20968  Utilities->CallLogPop(1570);
20969  if(ExitList.size() < 4)
20970  {
20971  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
20972  return("");
20973  }
20974  else
20975  {
20976  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
20977  return("");
20978  }
20979  }
20980  }
20981  Utilities->CallLogPop(1569);
20982  if(ExitList.size() < 4)
20983  {
20984  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
20985  return(" at " + StartName);
20986  }
20987  else
20988  {
20989  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
20990  return(" at " + StartName);
20991  }
20992 }
20993 
20994 // ---------------------------------------------------------------------------
20995 /* can't trust this as locations within a vector may not be contiguous
20996  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
20997  {
20998  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
20999  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
21000  //must be preceded by a TimeLoc departure
21001  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
21002  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
21003  {
21004  if((AVPtr + x) < TDEPtr->ActionVector.end())
21005  {
21006  AnsiString xx = (AVPtr + x)->Command;//test
21007  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
21008  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
21009  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
21010  {
21011  Utilities->CallLogPop();
21012  return false;
21013  }
21014  else if((AVPtr + x)->SequenceType == FinishSequence)
21015  {
21016  Utilities->CallLogPop();
21017  return true;
21018  }
21019  }
21020  }
21021  Utilities->CallLogPop();
21022  return false;
21023  }
21024 */
21025 // ---------------------------------------------------------------------------
21026 
21027 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
21028 {
21029  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
21030  AnsiString FormatStr = "####0.0";
21031  AnsiString AvLateArrMins = "";
21032  AnsiString AvEarlyArrMins = "";
21033  AnsiString AvLatePassMins = "";
21034  AnsiString AvEarlyPassMins = "";
21035  AnsiString AvLateDepMins = "";
21036  AnsiString AvLateExitMins = "";
21037  AnsiString AvEarlyExitMins = "";
21038 
21039  //calculate remaining CumulativeDelayedRandMinsAllTrains for trains still in vector (CumulativeDelayedRandMinsAllTrains for exited or removed trains already accounted for)
21040  for(unsigned int x = 0; x < TrainVector.size(); x++)
21041  {
21042  Utilities->CumulativeDelayedRandMinsAllTrains += int(TrainVectorAt(89, x).CumulativeDelayedRandMinsOneTrain);
21043  }
21044 
21045  if(LateArrivals > 0)
21046  {
21047  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
21048  }
21049  if(EarlyArrivals > 0)
21050  {
21051  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
21052  }
21053  if(LatePasses > 0)
21054  {
21055  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
21056  }
21057  if(EarlyPasses > 0)
21058  {
21059  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
21060  }
21061  if(LateDeps > 0)
21062  {
21063  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
21064  }
21065  if(LateExits > 0) //added at v2.9.1
21066  {
21067  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
21068  }
21069  if(EarlyExits > 0) //added at v2.9.1
21070  {
21071  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
21072  }
21073  PerfFile << '\n' << '\n' << "***************************************";
21074  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
21075 
21076  if(OnTimeArrivals != 1)
21077  {
21078  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
21079  }
21080  else
21081  {
21082  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
21083  }
21084  if(LateArrivals > 1)
21085  {
21086  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
21087  }
21088  else if(LateArrivals == 1)
21089  {
21090  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
21091  }
21092  else
21093  {
21094  PerfFile << LateArrivals << " late arrivals" << '\n';
21095  }
21096  if(EarlyArrivals > 1)
21097  {
21098  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
21099  }
21100  else if(EarlyArrivals == 1)
21101  {
21102  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
21103  }
21104  else
21105  {
21106  PerfFile << EarlyArrivals << " early arrivals" << '\n';
21107  }
21108  if(OnTimePasses != 1)
21109  {
21110  PerfFile << OnTimePasses << " on-time passes" << '\n';
21111  }
21112  else
21113  {
21114  PerfFile << OnTimePasses << " on-time pass" << '\n';
21115  }
21116  if(LatePasses > 1)
21117  {
21118  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
21119  }
21120  else if(LatePasses == 1)
21121  {
21122  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
21123  }
21124  else
21125  {
21126  PerfFile << LatePasses << " late passes" << '\n';
21127  }
21128  if(EarlyPasses > 1)
21129  {
21130  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
21131  }
21132  else if(EarlyPasses == 1)
21133  {
21134  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
21135  }
21136  else
21137  {
21138  PerfFile << EarlyPasses << " early passes" << '\n';
21139  }
21140 
21141  if(OnTimeExits != 1) //this batch added at v2.9.1
21142  {
21143  PerfFile << OnTimeExits << " on-time exits" << '\n';
21144  }
21145  else
21146  {
21147  PerfFile << OnTimeExits << " on-time exit" << '\n';
21148  }
21149  if(LateExits > 1)
21150  {
21151  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
21152  }
21153  else if(LateExits == 1)
21154  {
21155  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
21156  }
21157  else
21158  {
21159  PerfFile << LateExits << " late exits" << '\n';
21160  }
21161  if(EarlyExits > 1)
21162  {
21163  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
21164  }
21165  else if(EarlyExits == 1)
21166  {
21167  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
21168  }
21169  else
21170  {
21171  PerfFile << EarlyExits << " early exits" << '\n';
21172  }
21173 
21174  if(OnTimeDeps != 1)
21175  {
21176  PerfFile << OnTimeDeps << " on-time departures" << '\n';
21177  }
21178  else
21179  {
21180  PerfFile << OnTimeDeps << " on-time departure" << '\n';
21181  }
21182  if(LateDeps > 1)
21183  {
21184  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
21185  }
21186  else if(LateDeps == 1)
21187  {
21188  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
21189  }
21190  else
21191  {
21192  PerfFile << LateDeps << " late departures" << '\n';
21193  }
21194  TDateTime TempExcessLCDownTime;
21195  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
21196  {
21197 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
21198  //later perf summaries with lower values, changed at v2.8.0
21199 // {
21200  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
21201 // }
21202 /*
21203  else
21204  {
21205  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
21206  }
21207 */
21208  if(TempExcessLCDownTime > TDateTime(0))
21209  {
21210  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
21211  }
21212  }
21213 
21214  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
21215 
21216  if(ExcessLCDownMins > 0.1)
21217  {
21218  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
21219  }
21220  if(Utilities->CumulativeDelayedRandMinsAllTrains > 0) //added at v2.13.0
21221  {
21222  PerfFile << Utilities->CumulativeDelayedRandMinsAllTrains << " minutes lost due to random delays when stopped at locations" << '\n';
21223  }
21224  if(MissedStops != 1)
21225  {
21226  PerfFile << MissedStops << " missed stops" << '\n';
21227  }
21228  else
21229  {
21230  PerfFile << MissedStops << " missed stop" << '\n';
21231  }
21232  if(OtherMissedEvents != 1)
21233  {
21234  PerfFile << OtherMissedEvents << " other missed events" << '\n';
21235  }
21236  else
21237  {
21238  PerfFile << OtherMissedEvents << " other missed event" << '\n';
21239  }
21240  if(SkippedTTEvents != 1)
21241  {
21242  PerfFile << SkippedTTEvents << " skipped timetable events" << '\n';
21243  }
21244  else
21245  {
21246  PerfFile << SkippedTTEvents << " skipped timetable event" << '\n';
21247  }
21248  if(UnexpectedExits != 1)
21249  {
21250  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
21251  }
21252  else
21253  {
21254  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
21255  }
21256  if(IncorrectExits != 1)
21257  {
21258  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
21259  }
21260  else
21261  {
21262  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
21263  }
21264  if(NumFailures != 1)
21265  {
21266  PerfFile << NumFailures << " train failures" << '\n';
21267  }
21268  else
21269  {
21270  PerfFile << NumFailures << " train failure" << '\n';
21271  }
21272  if(AvHoursIntValue > 0)
21273  {
21274  if(AvHoursIntValue == 1)
21275  {
21276  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
21277  }
21278  else
21279  {
21280  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
21281  }
21282  }
21283  AnsiString AvLateMinsLocsNotReached = "";
21284 
21286  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
21287  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
21288 
21289  if(LocsNotReached > 0)
21290  {
21291  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
21292  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
21293  }
21294  if(SPADRisks != 1)
21295  {
21296  PerfFile << SPADRisks << " SPAD risks" << '\n';
21297  }
21298  else
21299  {
21300  PerfFile << SPADRisks << " SPAD risk" << '\n';
21301  }
21302  if(SPADEvents != 1)
21303  {
21304  PerfFile << SPADEvents << " SPADs" << '\n';
21305  }
21306  else
21307  {
21308  PerfFile << SPADEvents << " SPAD" << '\n';
21309  }
21310  if(Derailments != 1)
21311  {
21312  PerfFile << Derailments << " derailments" << '\n';
21313  }
21314  else
21315  {
21316  PerfFile << Derailments << " derailment" << '\n';
21317  }
21318  if(CrashedTrains != 1)
21319  {
21320  PerfFile << CrashedTrains << " crashed trains" << '\n';
21321  }
21322  else
21323  {
21324  PerfFile << CrashedTrains << " crashed train" << '\n';
21325  }
21326  PerfFile << '\n' << "***************************************" << '\n';
21327 
21328  bool DerailSPADFlag = false, CrashFlag = false;
21329 
21330  int OverallScorePercent = 100;
21331  int TotArrDepExit = 0;
21332  double TotLateMinsFactor = 1;
21333  double MissedStopAndSPADRiskFactor = 1;
21334  double NetNegFactor = 1;
21335 
21337  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1, passes not counted
21338  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
21339  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
21340  // 'no timetabled departures... message, which was inappropriate
21341 
21342  if((SPADEvents > 0) || (Derailments > 0))
21343  {
21344  OverallScorePercent = 5; // overrides other calculations
21345  DerailSPADFlag = true;
21346  }
21347  if(CrashedTrains > 0)
21348  {
21349  OverallScorePercent = 0; // overrides other calculations
21350  CrashFlag = true;
21351  }
21352  if(OverallScorePercent == 100)
21353  {
21354  int LatenessPenalty = TotLateArrMins + TotLateDepMins; //added at v2.13.0 for random delays
21355  if(Utilities->CumulativeDelayedRandMinsAllTrains > LatenessPenalty)
21356  {
21357  LatenessPenalty = 0;
21358  }
21359  else
21360  {
21361  LatenessPenalty -= Utilities->CumulativeDelayedRandMinsAllTrains;
21362  }
21363  if(TotArrDepExit > 0)
21364  {
21365  TotLateMinsFactor = exp((-0.1732) * (LatenessPenalty + OperatingTrainLateMins + NotStartedTrainLateMins + TotLateExitMins +
21366  ((OtherMissedEvents + SkippedTTEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
21367  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
21368  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
21369  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
21370  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
21371  // of arrivals & departures, where 4% = half, 8% = a quarter etc
21372  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
21373  // NetNegfactor: product of the above two
21374  OverallScorePercent = 100 * NetNegFactor;
21375  }
21376  }
21377  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
21378  // flag condits added at v1.1.4 - see above for what the error was
21379  {
21380  AnsiString OneFailureString = ", though the failure would account for some poor performance";
21381  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
21382  AnsiString AddedString = "";
21383  if(NumFailures == 1)
21384  {
21385  AddedString = OneFailureString;
21386  }
21387  if(NumFailures > 1)
21388  {
21389  AddedString = TwoOrMoreFailureString;
21390  }
21391  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
21392  AnsiString Rating = "";
21393  if(OverallScorePercent == 100)
21394  {
21395  Rating = "Perfect!";
21396  }
21397  else if(OverallScorePercent >= 95)
21398  {
21399  Rating = "Excellent";
21400  }
21401  else if(OverallScorePercent >= 90)
21402  {
21403  Rating = "Very good";
21404  }
21405  else if(OverallScorePercent >= 80)
21406  {
21407  Rating = "Good";
21408  }
21409  else if(OverallScorePercent >= 70)
21410  {
21411  Rating = "Fair";
21412  }
21413  else if(OverallScorePercent >= 60)
21414  {
21415  Rating = "Unacceptable" + AddedString;
21416  }
21417  else if(OverallScorePercent >= 50)
21418  {
21419  Rating = "Poor" + AddedString;
21420  }
21421  else if(OverallScorePercent >= 40)
21422  {
21423  Rating = "Bad" + AddedString;
21424  }
21425  else if(OverallScorePercent >= 30)
21426  {
21427  Rating = "Very bad" + AddedString;
21428  }
21429  else if(OverallScorePercent >= 20)
21430  {
21431  Rating = "Terrible" + AddedString;
21432  }
21433  else if(OverallScorePercent >= 10)
21434  {
21435  Rating = "Appalling" + AddedString;
21436  }
21437  else if(OverallScorePercent >= 5)
21438  {
21439  if(DerailSPADFlag)
21440  {
21441  Rating = "Disastrous - potential loss of life";
21442  }
21443  // SPADs/Derailments
21444  else
21445  {
21446  Rating = "Dire" + AddedString;
21447  }
21448  }
21449  else if(OverallScorePercent < 5)
21450  {
21451  if(CrashFlag)
21452  {
21453  Rating = "Catastrophic - loss of life"; // Crashes
21454  }
21455  else
21456  {
21457  Rating = "Abysmal";
21458  }
21459  }
21460  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
21461  }
21462  else
21463  {
21464  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
21465  }
21466  PerfFile << '\n' << "***************************************";
21467  PerfFile.flush();
21468  Utilities->CallLogPop(1736);
21469 }
21470 
21471 // ---------------------------------------------------------------------------
21472 
21474 {
21475  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
21476  for(unsigned int x = 0; x < TrainVector.size(); x++)
21477  {
21478  TTrain &Train = TrainVectorAt(58, x);
21479  if(Train.Crashed)
21480  // can't use background colours for crashed & derailed because same colour
21481  {
21482  CrashWarning = true;
21483  }
21484  else if(Train.Derailed)
21485  // can't use background colours for crashed & derailed because same colour
21486  {
21487  DerailWarning = true;
21488  }
21489  else if(Train.BackgroundColour == clSPADBackground)
21490  // use colour as that changes as soon as passes signal
21491  {
21492  SPADWarning = true;
21493  }
21494  else if(Train.BackgroundColour == clTrainFailedBackground)
21495  {
21496  TrainFailedWarning = true;
21497  }
21498  else if(Train.BackgroundColour == clCallOnBackground)
21499  // use colour as also stopped at signal
21500  {
21501  CallOnWarning = true;
21502  }
21503  else if(Train.BackgroundColour == clSignalStopBackground)
21504  // use colour to distinguish from call-on
21505  {
21506  SignalStopWarning = true;
21507  }
21508  else if(Train.BackgroundColour == clBufferAttentionNeeded)
21509  // use colour to distinguish from ordinary buffer stop
21510  {
21511  BufferAttentionWarning = true;
21512  }
21513  }
21514  Utilities->CallLogPop(1796);
21515 }
21516 
21517 // ---------------------------------------------------------------------------
21518 
21520 {
21521  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
21522 
21523  // calculate lateness for running trains
21526  for(unsigned int x = 0; x < TrainVector.size(); x++)
21527  {
21528  TTrain &Train = TrainVectorAt(64, x);
21529  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
21530  AVEntryPtr++)
21531  {
21532  if(AVEntryPtr < Train.ActionVectorEntryPtr)
21533  {
21534  continue;
21535  }
21536  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.RevisedStoppedAtLoc() && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
21537  TTClockTime))
21538  {
21539  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
21541  }
21542 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
21543  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
21544  TTClockTime))
21545  {
21546  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
21547  OperatingTrainArrDep++;
21548  }
21549 */
21550  }
21551  }
21552 
21553  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
21556 
21557  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
21558  {
21559  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
21560  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
21561  int IncrementalMinutes = 0;
21562  if(AVEntryLast.FormatType == Repeat)
21563  {
21564  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
21565  }
21566  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
21567  {
21568  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
21569  if(TTOD.RunningEntry != NotStarted)
21570  {
21571  continue;
21572  }
21573  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
21574  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
21575  bool TrainOperatingFlag = false;
21576  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
21577  {
21578  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
21579  {
21580  TrainOperatingFlag = true;
21581  break;
21582  }
21583  }
21584  if(TrainOperatingFlag)
21585  {
21586  continue;
21587  }
21588  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
21589  {
21590  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
21591  }
21592  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
21593  {
21594  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
21595  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
21596  {
21597  break; // all the rest will also be greater (& default of -1 will be less)
21598  }
21599  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
21600  {
21601  break; // all the rest will also be greater (& default of -1 will be less)
21602  }
21603  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
21604  {
21605  break; // all the rest will also be greater (& default of -1 will be less)
21606  }
21607  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
21608  {
21609  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
21611  }
21612 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
21613  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
21614  {
21615  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
21616  NotStartedTrainArrDep++;
21617  }
21618 */
21619  }
21620  }
21621  }
21622  Utilities->CallLogPop(1894);
21623 }
21624 
21625 // ---------------------------------------------------------------------------
21626 
21628 // new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2.13.0)
21629 // clears entries then adds values for running trains then for continuation entries
21630 // dont limit size here as need to check all trains (ActionsDueListBox is limited to 20 trains in Interface.cpp)
21631 {
21632  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
21633  OpTimeToActMultiMap.clear();
21634  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
21635 
21636  if(!TrainVector.empty())
21637  // build OpTimeToActMultiMap entries for running trains
21638  {
21639  AnsiString HeadCode;
21640  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
21641  int TrainID;
21642  THCandTrainPosParam HCandTrainPosParam;
21643  for(unsigned int x = 0; x < TrainVector.size(); x++)
21644  {
21645  HeadCode = TrainVectorAt(62, x).HeadCode;
21646  TrainID = TrainVectorAt(63, x).TrainID;
21647  HCandTrainPosParam.first = HeadCode;
21648  HCandTrainPosParam.second = TrainID;
21649  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
21650  if((TimeToAct >= 0) && (TimeToAct < 59.9))
21651  // -1 indicates don't display
21652  {
21653  OpTimeToActMultiMapEntry.first = TimeToAct;
21654  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
21655  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
21656  }
21657  }
21658  }
21659 /*
21660  * class TContinuationTrainExpectationEntry
21661  {
21662  public:
21663  AnsiString Description; ///< service description
21664  AnsiString HeadCode; ///< service headcode
21665  int RepeatNumber; ///< service RepeatNumber
21666  int IncrementalMinutes; ///< Repeat separation in minutes
21667  int IncrementalDigits; ///< Repeat headcode separation
21668  int VectorPosition; ///< TrackVectorPosition for the continuation element
21669  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
21670  };
21671 
21672  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
21673  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
21674  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
21675  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
21676 */
21677 
21679  // build OpTimeToActMultiMap entries for expected trains
21680  {
21681  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
21682  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
21683  float TimeToAct = 0; // minutes
21684  int DistanceToRedSignal = 0; // metres
21685  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
21686  // used to ensure only one train displayed for a given continuation
21687  ContinuationEntryVecPosVector.clear();
21688  bool LaterTrain = false;
21691  {
21692  LaterTrain = false;
21693  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
21694  {
21695  CTEIt++;
21696  continue; // not interested in running or exited trains
21697  }
21698  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
21699  {
21700  CTEIt++;
21701  continue;
21702  // don't include trains not entered yet when a train is already on the continuation
21703  }
21704  if(!ContinuationEntryVecPosVector.empty())
21705  {
21706  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
21707  {
21708  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
21709  {
21710  LaterTrain = true;
21711  ;
21712  // skip past remaining trains waiting to enter at same point
21713  break;
21714  }
21715  }
21716  }
21717  if(LaterTrain)
21718  {
21719  CTEIt++;
21720  continue;
21721  }
21722  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
21723  AnsiString HeadCode = CTEIt->second.HeadCode;
21724  float CurrentStopTime; // set to 0 at start of function
21725  float LaterStopTime; // set to 0 at start of function
21726  float RecoverableTime; // set to 0 at start of function
21727  int AvTrackSpeed; // set to 0 at start of function
21728  int TrainID = -1; // not yet allocated for train still to enter
21729  int DistanceToExit; //not used for continuation entries
21730  THVShortPair ExitPair;
21731  bool SigControlAndCanPassRedSignal = false; // doesn't apply for a continuation
21732 
21733 //at v2.11.0 found that with *AVPtr set to ...ActionVector.at(0) below instead of ...at(1) to stop signaller control trains throwing an error (because there is no ...at(1) -
21734 //discovered with Birmingham) the LaterStopTime isn't calculated and if a train does something other than depart after an arrival it is still listed in the actions due panel -
21735 //because it just calcs the distance to the red signal and converts that to a time. So here (after v2.11.0) this new test is introduced to determine whether a train is a
21736 //signaller control train (when the ActionVector size is 1) or not (ActionVector size > 1), and the ...at(value) is set accordingly - 0 for signaller control or 1 if not.
21737 
21738  int AtValue = 1;
21739  if(CTEIt->second.TrainDataEntryPtr->ActionVector.size() == 1)
21740  {
21741  AtValue = 0;
21742  }
21743  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
21744  // EntryPos always 0 for entering at a continuation
21745  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(AtValue), //see above note
21746  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
21747  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
21748  // for a train it's the one in front of LeadElement
21749  if(AvTrackSpeed < 30)
21750  {
21751  AvTrackSpeed = 30;
21752  }
21753  if(DistanceToRedSignal == -1)
21754  {
21755  TimeToAct = 60.0;
21756  }
21757  else
21758  {
21759  int Speed = AvTrackSpeed;
21760  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
21761  if(AvTrackSpeed > MaxSpeed)
21762  {
21763  Speed = MaxSpeed;
21764  }
21765  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(0).SignallerControl) //changed to ...at(0) from at(1) at v2.11.0 as SignallerControl only valid for ..at(0)
21766  // defined in timetable as under signaller control
21767  {
21768  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
21769  LaterStopTime = 0;
21770  }
21771  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
21772  // accel & decel taken into account in
21773  // CalcDistanceToRedSignalandStopTime
21774  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
21775  // don't need CurrentStopTime or RecoverableTime for continuation entries
21776  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
21777  TimeToAct += MinsBefEnter;
21778  }
21779  THCandTrainPosParam HCandTrainPosParam;
21780  HCandTrainPosParam.first = HeadCode;
21781  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
21782  // -1-CTE... because 2nd value covers TrainID if +ve &
21783  // continuation track vector position if -ve, -1 allows for vecpos being 0
21784  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
21785  {
21786  OpTimeToActMultiMapEntry.first = TimeToAct;
21787  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
21788  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
21789  }
21790  CTEIt++;
21791  }
21792  }
21793  Utilities->CallLogPop(2081);
21794 }
21795 
21796 // ---------------------------------------------------------------------------
21797 
21799 // new for multiplayer
21800 // clears entries then adds values for running trains
21801 {
21802  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildTimeToExitMultiMap");
21803  TimeToExitMultiMap.clear();
21804  TTimeToExitMultiMapEntry TimeToExitMultiMapEntry;
21805 
21806  if(!TrainVector.empty())
21807  // build map entries for running trains
21808  {
21809  TExitInfo ExitInfo; //corresponds to TServiceInfo in Interface
21810  THVShortPair ExitPair;
21811  float TimeToExit;
21812  for(unsigned int x = 0; x < TrainVector.size(); x++)
21813  {
21815  ExitInfo.RepeatNumber = short(TrainVectorAt(81, x).RepeatNumber);
21816  ExitInfo.TimeToExitSecs = short(TrainVectorAt(77, x).TimeToExit * 60);
21817  ExitPair = TrainVectorAt(76, x).ExitPair;
21818  if((ExitInfo.TimeToExitSecs >= 3570) || (ExitInfo.TimeToExitSecs < 1)) //59.5 mins or -60 secs
21819  {
21820  ExitInfo.TimeToExitSecs = -1;
21821  }
21822  TimeToExitMultiMapEntry.first = ExitPair;
21823  TimeToExitMultiMapEntry.second = ExitInfo;
21824  TimeToExitMultiMap.insert(TimeToExitMultiMapEntry);
21825  }
21826  }
21827  Utilities->CallLogPop(2323);
21828 }
21829 
21830 // ---------------------------------------------------------------------------
21831 
21832 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
21833  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
21834  float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
21835 // new v2.2.0
21836 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
21837 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
21838 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
21839 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
21840 // aren't used - this means there is no display for the train in question
21841 {
21842  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
21843  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
21844  int DistanceToRedSignal = 0;
21845  DistanceToExit = -1;
21846  ExitPair.first = -1;
21847  ExitPair.second = -1;
21848  int CumTrackSpeed = 0;
21849  // average track speed, in case need to use in time calc
21850  int TrackSpeedCount = 0;
21851  float KmPerLocationStop;
21852  float MaxAllowableSpeed;
21853 
21854  //below added at v2.6.1
21855  if(TrainID > -1)
21856  {
21857  TTrain &Train = TrainVectorAtIdent(51, TrainID);
21858  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
21859  Train.StationStopCalculated = false;
21860  }
21861  AvTrackSpeed = 0;
21862  int CurrentElement = TrackVectorPosition;
21863  int CurrentEntryPos = TrackVectorPositionEntryPos;
21864  int NextElement;
21865  int NextEntryPos;
21866  int NextExitPos;
21867 
21868  CurrentStopTime = 0;
21869  LaterStopTime = 0;
21870  RecoverableTime = 0;
21871  if(CurrentElement == -1) // end element, no action needed
21872  {
21873  Utilities->CallLogPop(2094);
21874  return(-1);
21875  }
21876  int CurrentExitPos;
21877 
21878  // get ExitPos for first element to be measured
21879  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
21880  {
21881  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
21882  {
21883  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
21884  {
21885  CurrentExitPos = 1;
21886  }
21887  else
21888  {
21889  CurrentExitPos = 3;
21890  }
21891  }
21892  else
21893  {
21894  CurrentExitPos = 0; // trailing point
21895  }
21896  }
21897  else
21898  {
21899  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
21900  }
21901  // get CumTrackSpeed for first measured element
21902 
21903  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
21904  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
21905  bool CurrentElementFailed = Track->TrackElementAt(1549, CurrentElement).Failed; //added at v2.13.2
21906 
21907  // check if currently stopped at a location, and if so add the remaining dwell time
21908  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
21909  if(TrainID > -1)
21910  // -1 for a continuation and can't be at a location as not yet entered
21911  {
21912  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
21914  // this used to deduct from RecoverableTime when arrive at a location
21915  if(Train.RevisedStoppedAtLoc())
21916  {
21917  if(Train.StoppedForTrainInFront)
21918  {
21919  Utilities->CallLogPop(2082);
21920  return(-1); // no action needed
21921  }
21922  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
21923  {
21924  Utilities->CallLogPop(2083);
21925  return(-1); // not due a departure so no action needed
21926  }
21927  else // due a departure
21928  {
21929  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
21930  // can't convert a TDateTime to a float directly
21931  CurrentStopTime = float(TimeToDepart);
21932  AVPtr++;
21933  }
21934  }
21935  }
21936  // check if CurrentElement is a red signal, but ok if autosig route after provided signal not failed
21937  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
21938  // ok if autosig route after red signal unless signal has failed
21939  {
21940  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
21941  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
21942  int RouteNumber; // holder for referenced value, not used
21943  if((AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !CurrentElementFailed)
21944  { //CurrentElementFailed added at v2.13.2
21945  Utilities->CallLogPop(2078);
21946  return(-1);
21947  }
21948  else if(SigControlAndCanPassRedSignal)
21949  // ignore signal and increment CurrentElement to NextElement
21950  {
21951  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
21952  {
21953  if((NextEntryPos == 0) || (NextEntryPos == 2))
21954  // leading entry point
21955  {
21956  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
21957  {
21958  NextExitPos = 1;
21959  }
21960  else
21961  {
21962  NextExitPos = 3;
21963  }
21964  }
21965  else
21966  {
21967  NextExitPos = 0; // trailing entry point
21968  }
21969  }
21970  else
21971  {
21972  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
21973  }
21974  CurrentElement = NextElement;
21975  CurrentEntryPos = NextEntryPos;
21976  CurrentExitPos = NextExitPos;
21977  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
21978  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
21979  }
21980  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
21981  // give 'NOW' indication after allowed to pass stop signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
21982  {
21983  Utilities->CallLogPop(2084);
21984  return(0);
21985  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
21986  }
21987  }
21988  int LaterStopNumber = 0;
21989  int x = 0;
21990  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
21991 
21992  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
21993  // not red signal next (in fwd direction) so enter loop to calc CumLength
21994  {
21995  x++; // added in v2.4.0 as above
21996  if(x > 5000)
21997  {
21998  Utilities->CallLogPop(2120);
21999  return(-1);
22000  }
22001  if(CurrentEntryPos > 1)
22002  {
22003  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
22004  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
22005  }
22006  else
22007  {
22008  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
22009  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
22010  }
22011  TrackSpeedCount++;
22012 
22013  //added for multiplayer - exiting at a continuation and continuation length already added
22014  if((Track->TrackElementAt(1407, CurrentElement).TrackType == Continuation) && (Track->TrackElementAt(1408, CurrentElement).Config[CurrentExitPos] == End))
22015  {
22016  DistanceToExit = DistanceToRedSignal; //don't need to exit function here as will exit when find that the next Conn value is -1
22017  ExitPair.first = Track->TrackElementAt(1409, CurrentElement).HLoc;
22018  ExitPair.second = Track->TrackElementAt(1410, CurrentElement).VLoc;
22019  //here repeat calcs for MaxAllowableSpeed & AvTrackSpeed as done at end for stop signal
22020  //need here as next element will be -1 so will exit before calcs at end
22021  if(TrackSpeedCount > 0)
22022  {
22023  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
22024  }
22025  else // shouldn't reach here but include to prevent divide by zero error
22026  {
22027  if(CurrentEntryPos > 1)
22028  {
22029  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
22030  }
22031  else
22032  {
22033  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
22034  }
22035  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
22036  }
22037  //calc AvTrackSpeed
22038  if(LaterStopNumber > 0)
22039  {
22040  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
22041  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
22042  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
22043  // average line speed/2 (for half distance accelerating and half decelerating.
22044  }
22045  else
22046  {
22047  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
22048  // using linear trendline for accel & decel distance at various speeds
22049  // at half braking, speed never < 60 using this
22050  }
22051  if(AvTrackSpeed > MaxAllowableSpeed)
22052  {
22053  AvTrackSpeed = MaxAllowableSpeed;
22054  }
22055  }
22056 
22057  // added at v2.6.1 to find DistanceToStationStop for trains running early
22058  if(TrainID > -1) //can ignore continuation entries as these don't run early
22059  {
22060  TTrain &Train = TrainVectorAtIdent(52, TrainID);
22061  if(!Train.StationStopCalculated)
22062  {
22063  if(Train.TrainMode == Timetable)
22064  {
22065  bool StopRequired = false;
22066  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
22067  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
22068  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
22069  {
22070  // no need to add in the length of element to CumulativeLength
22071  if(StopRequired)
22072  {
22073  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
22074  Train.StationStopCalculated = true; //don't want to update it with later stops
22075  }
22076  }
22077  }
22078  }
22079  }
22080  // check for train in front, but if on a bridge on other track then ok
22081  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
22082  int TrainOnElement;
22083  if(TE.TrackType != Bridge)
22084  {
22085  TrainOnElement = TE.TrainIDOnElement;
22086  }
22087  else
22088  {
22089  if(CurrentEntryPos > 1)
22090  {
22092  }
22093  else
22094  {
22096  }
22097  }
22098  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
22099  // train in front before red signal
22100  {
22101  Utilities->CallLogPop(2085);
22102  return(-1);
22103  }
22104  // add to stoptime if required
22105  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
22106  {
22107  double StopTimeDouble;
22108  while(AVPtr->FormatType == PassTime)
22109  {
22110  AVPtr++; // skip past any passes
22111  }
22112  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
22113  (AVPtr->FormatType == TimeTimeLoc)))
22114  // stop due here so calc dwell time & advance Ptr
22115  {
22116  if(AVPtr->FormatType == TimeTimeLoc)
22117  {
22118  LaterStopNumber++;
22119  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
22120  if(StopTimeDouble < 0.5)
22121  {
22122  StopTimeDouble = 0.5;
22123  }
22124  // at least 30 secs delay at station
22125  // can't convert a TDateTime to a float directly
22126  LaterStopTime += float(StopTimeDouble);
22127  RecoverableTime += StopTimeDouble - 0.5;
22128  if((LaterStopNumber == 1) && (TrainID > -1))
22129  {
22130  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
22131  }
22132  AVPtr++;
22133  }
22134  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
22135  {
22136  if((AVPtr + 1)->FormatType == TimeLoc)
22137  // must be a departure
22138  {
22139  LaterStopNumber++;
22140  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
22141  // can't convert a TDateTime to a float directly
22142  if(StopTimeDouble < 0.5)
22143  {
22144  StopTimeDouble = 0.5;
22145  }
22146  // at least 30 secs delay at station
22147  LaterStopTime += float(StopTimeDouble);
22148  RecoverableTime += StopTimeDouble - 0.5;
22149  if((LaterStopNumber == 1) && (TrainID > -1))
22150  {
22151  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
22152  }
22153  AVPtr++;
22154  AVPtr++;
22155  }
22156  else // not a departure, does something else at the location so no calculation needed
22157  {
22158  Utilities->CallLogPop(2086);
22159  return(-1);
22160  }
22161  }
22162  }
22163  }
22164  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
22165  if(NextElement == -1) // reached end element, no action needed
22166  {
22167  Utilities->CallLogPop(2077);
22168  return(-1);
22169  }
22170  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
22171  // get NextExitPos
22172  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
22173  {
22174  if((NextEntryPos == 0) || (NextEntryPos == 2))
22175  // leading entry point
22176  {
22177  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
22178  {
22179  NextExitPos = 1;
22180  }
22181  else
22182  {
22183  NextExitPos = 3;
22184  }
22185  }
22186  else
22187  {
22188  NextExitPos = 0; // trailing entry point
22189  }
22190  }
22191  else
22192  {
22193  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
22194  }
22195  CurrentElement = NextElement;
22196  CurrentEntryPos = NextEntryPos;
22197  CurrentExitPos = NextExitPos;
22198  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
22199  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
22200  CurrentElementFailed = Track->TrackElementAt(1550, CurrentElement).Failed; //added at v2.13.2
22201  }
22202  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
22203  // ok if autosig route after red signal, no action needed
22204  {
22205  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
22206  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
22207  int RouteNumber; // holder for referenced value, not used
22208  if((AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !CurrentElementFailed)
22209  { //CurrentElementFailed added at v2.13.2
22210  Utilities->CallLogPop(2095);
22211  return(-1);
22212  }
22213  }
22214 
22215  if(TrackSpeedCount > 0)
22216  {
22217  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
22218  }
22219  else // shouldn't reach here but include to prevent divide by zero error
22220  {
22221  if(CurrentEntryPos > 1)
22222  {
22223  MaxAllowableSpeed = Track->TrackElementAt(1433, CurrentElement).SpeedLimit23;
22224  }
22225  else
22226  {
22227  MaxAllowableSpeed = Track->TrackElementAt(1434, CurrentElement).SpeedLimit01;
22228  }
22229  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
22230  }
22231 
22232  if(LaterStopNumber > 0)
22233  {
22234  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
22235  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
22236  }
22237  else
22238  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
22239  // average line speed/2 (for half distance accelerating and half decelerating.
22240  {
22241  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
22242  // using linear trendline for accel & decel distance at various speeds
22243  // at half braking, speed never < 60 using this
22244  }
22245  if(AvTrackSpeed > MaxAllowableSpeed)
22246  {
22247  AvTrackSpeed = MaxAllowableSpeed;
22248  }
22249  Utilities->CallLogPop(2096);
22250  return(DistanceToRedSignal);
22251 }
22252 
22253 // ---------------------------------------------------------------------------
22254 // end of TTrainController entries
22255 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:9411
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:18842
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:137
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:385
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:52
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:16075
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:352
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:351
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:494
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:706
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:72
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1735
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:904
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:175
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:15743
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller, bool NoLogFlag)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6522
TActionVectorEntry::SplitDistribution
AnsiString SplitDistribution
Definition: TrainUnit.h:125
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:373
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2....
Definition: TrainUnit.cpp:21627
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:816
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:17179
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:92
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:53
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:1017
Create
@ Create
Definition: TrainUnit.h:52
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:793
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:19847
Arrive
@ Arrive
Definition: TrainUnit.h:52
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:52
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:350
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:391
Depart
@ Depart
Definition: TrainUnit.h:52
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:721
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:12501
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:719
PerfLogForm
TPerfLogForm * PerfLogForm
Definition: PerfLogUnit.cpp:11
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:292
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:435
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:803
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:286
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7402
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:53
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:910
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1664
IntermediateSequence
@ IntermediateSequence
Definition: TrainUnit.h:78
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:729
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:143
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:785
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1676
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:67
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:67
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:511
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:754
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:68
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:439
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:72
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:20601
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:15001
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:153
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:373
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:1003
TTrain::TrainFailed
bool TrainFailed
Definition: TrainUnit.h:419
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:986
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:802
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:346
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:982
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:20919
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:8287
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:393
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:485
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:373
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6652
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:802
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:10694
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:322
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:901
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route
Definition: TrainUnit.h:728
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:16910
TExitInfo::TimeToExitSecs
short TimeToExitSecs
Definition: TrainUnit.h:111
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:860
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:1009
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:826
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:1016
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:185
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:19638
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:802
TTrainController::ControllerGetNewServiceDepartureInfo
AnsiString ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::GetNewServiceDepartureInfo for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:10889
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:12454
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1656
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:9383
TNumListIterator
TNumList::iterator TNumListIterator
Definition: TrainUnit.h:98
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:137
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:969
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:750
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:16925
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:268
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:967
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6445
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:983
TTrainController::GetServiceFromVector
TTrainDataEntry GetServiceFromVector(AnsiString Caller, AnsiString HeadCode, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
Return the TrainDataVector entry corresponding to ServiceReference, FinishType is 0 for end of servic...
Definition: TrainUnit.cpp:20275
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3593
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:688
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:67
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:507
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:467
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:10656
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:54
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:500
NotStarted
@ NotStarted
Definition: TrainUnit.h:89
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10979
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:854
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:796
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:353
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:724
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:379
TTrainDataEntry
Contains all data for a single timetable service entry.
Definition: TrainUnit.h:208
LeadMid
@ LeadMid
Definition: TrainUnit.h:301
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:300
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:844
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:20114
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:15823
TTrack::ResetAllTrainIDsAndFailedPointOrigSpeedLimits
void ResetAllTrainIDsAndFailedPointOrigSpeedLimits(int Caller)
Definition: TrackUnit.cpp:7774
TTrainController::RebuildTimeToExitMultiMap
void RebuildTimeToExitMultiMap(int Caller)
new for multiplayer
Definition: TrainUnit.cpp:21798
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:981
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6673
TOneRoute::RouteID
int RouteID
the ID number of the route, this is needed for session saves
Definition: TrackUnit.h:1551
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:746
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:1023
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:255
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:1019
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:218
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:864
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:821
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:736
TTrain::SkippedDeparture
bool SkippedDeparture
< used for terminating a service early and becoming new follow-on service
Definition: TrainUnit.h:334
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:9860
ChangeDescription
@ ChangeDescription
Definition: TrainUnit.h:55
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:17028
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:301
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:222
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:901
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:16966
StartNew
@ StartNew
Definition: TrainUnit.h:67
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:52
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:301
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:807
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:354
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:53
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:455
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:731
TTrainController::TimeToExitMultiMap
TTimeToExitMultiMap TimeToExitMultiMap
Map of times to exit & exit coordinates.
Definition: TrainUnit.h:1006
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:19386
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:11980
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:992
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5939
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:514
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:187
TTrain
Definition: TrainUnit.h:307
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:989
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:11999
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:299
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11715
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:896
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:253
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:878
TPerfLogForm::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: PerfLogUnit.cpp:32
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:491
GapJump
@ GapJump
Definition: TrackUnit.h:66
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:1010
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:847
NoSequence
@ NoSequence
Definition: TrainUnit.h:78
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:293
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:151
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:151
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:415
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6396
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:10287
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:373
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1703
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:373
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:295
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:367
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:228
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:709
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:371
TExitInfo::TExitInfo
TExitInfo()
Definition: TrainUnit.cpp:63
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:17007
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:840
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:330
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:175
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:791
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:471
TExitInfo::RepeatNumber
short RepeatNumber
Definition: TrainUnit.h:110
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:724
TTrain::LastSigPassedFailed
bool LastSigPassedFailed
flag used to erase route elements in an autosigs route after a failed signal
Definition: TrainUnit.h:401
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:16730
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1411
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:6210
End
@ End
Definition: TrackUnit.h:76
TUtilities::FixedMinRepairTime
int FixedMinRepairTime
Definition: Utilities.h:72
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:405
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:7291
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8951
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:348
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10704
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:68
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:304
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:411
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:17090
FailMissedDSC
@ FailMissedDSC
Definition: TrainUnit.h:41
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:125
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:977
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:788
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:1027
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:304
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:852
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:210
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:841
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:429
SignalPost
@ SignalPost
Definition: TrackUnit.h:66
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:971
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled event, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:475
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:144
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1680
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:356
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:13142
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:999
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:210
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5657
TUtilities::MinorDelayFactor
float MinorDelayFactor
Definition: Utilities.h:53
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:298
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool SetDataAndCheckLocations, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:16098
TTrainController::CheckFourthValidityForSplit
bool CheckFourthValidityForSplit(AnsiString SplitDistributionString, bool GiveMessages)
Checks fourth segment in timetable for train splits - percentage mass then '-' then percentage power ...
Definition: TrainUnit.cpp:12409
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:151
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:855
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:777
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:905
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:427
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:75
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:487
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:65
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:850
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:816
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:898
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1925
TExitInfo
Definition: TrainUnit.h:107
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:291
TUtilities::ModerateDelayCutoff
float ModerateDelayCutoff
Definition: Utilities.h:51
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:491
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:848
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:305
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:987
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:296
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:67
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:94
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:137
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:491
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:17906
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:965
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:465
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:990
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:11255
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:748
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:373
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:463
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:82
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:1015
Pass
@ Pass
Definition: TrainUnit.h:54
TTrain::SkipPtrValue
int SkipPtrValue
stores the pointer increment from first action in ActionVector for skipped actions when a departure i...
Definition: TrainUnit.h:381
TNumList
std::list< int > TNumList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:94
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:802
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:762
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:73
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:3241
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11427
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:832
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:70
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:1002
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:498
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:780
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:886
TUtilities::ModerateDelayFactor
float ModerateDelayFactor
Definition: Utilities.h:54
TUtilities::MajorDelayCutoff
float MajorDelayCutoff
Definition: Utilities.h:52
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:975
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:722
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:361
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
Definition: TrainUnit.h:322
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:1008
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:827
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:9395
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:1011
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:214
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:67
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:224
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:7143
TTrainController::MTBFHours
double MTBFHours
Mean time between train failures in timetable clock hours.
Definition: TrainUnit.h:819
TTrack::TInfrastructureFailureEntry::TVPos
int TVPos
Definition: TrackUnit.h:714
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:18434
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:479
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:16367
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10845
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:807
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:314
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3449
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:1022
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:473
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:5242
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2762
RearSplit
@ RearSplit
Definition: TrainUnit.h:52
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:135
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:54
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:827
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:672
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:972
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:825
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:995
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:135
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:55
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10739
TUtilities::FailureMode
TFailureMode FailureMode
specifies whether no failures or minor, moderate or major random failures are to be applied (added at...
Definition: Utilities.h:119
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:3173
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:7115
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:16465
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:54
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:893
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:165
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:846
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:870
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:387
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3543
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:968
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:9459
TUtilities::SignalChangeEventsPerFailure
int SignalChangeEventsPerFailure
number of signal changes between failures - reciprocal of failure probability per change
Definition: Utilities.h:95
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:338
TTrack::TInfrastructureFailureEntry
Definition: TrackUnit.h:713
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:9205
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:8484
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:3165
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:831
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:494
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrainController::TrainDataVectorCopy
TTrainDataVector TrainDataVectorCopy
vector containing the internal timetable, the copy is used for conflict analysis only
Definition: TrainUnit.h:884
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:469
NewService
@ NewService
Definition: TrainUnit.h:52
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:17243
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1513
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:491
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:389
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1663
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:377
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:226
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:222
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:838
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:302
TUtilities::CumulativeDelayedRandMinsAllTrains
int CumulativeDelayedRandMinsAllTrains
the running total of all random delays including knock-on delays for all trains, used to reduce total...
Definition: Utilities.h:101
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:170
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:83
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:1012
Crossover
@ Crossover
Definition: TrackUnit.h:66
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4842
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:742
AtLocation
@ AtLocation
Definition: TrainUnit.h:73
Signal
@ Signal
Definition: TrackUnit.h:76
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:974
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:1005
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:795
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:19079
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
Definition: TrainUnit.h:884
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:20554
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1664
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:471
Exited
@ Exited
Definition: TrainUnit.h:89
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10690
TTrain::ActionsSkippedFlag
bool ActionsSkippedFlag
prevents any further skipping until after the next departure
Definition: TrainUnit.h:336
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:16941
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:8771
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:18905
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:1024
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2380
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:491
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:60
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1685
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:964
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:491
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:760
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:316
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:804
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:52
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:437
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:816
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:994
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:970
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:373
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:67
TTrain::NewDelay
double NewDelay
an additional random delay at a location (added at v2.13.0)
Definition: TrainUnit.h:447
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:1006
Leave
@ Leave
Definition: TrainUnit.h:52
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:857
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:772
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:800
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel (OperatorA...
Definition: TrainUnit.h:461
TTrack::FailedSignalsVector
TFailedElementVector FailedSignalsVector
Definition: TrackUnit.h:793
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:403
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:3146
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:973
FNil
@ FNil
Definition: Utilities.h:43
NoFormat
@ NoFormat
Definition: TrainUnit.h:67
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:812
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:67
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2691
TTrain::TimeToExit
float TimeToExit
in minutes: new for multiplayer, -1 = > 60 mins
Definition: TrainUnit.h:459
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:491
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3572
Enter
@ Enter
Definition: TrainUnit.h:52
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:518
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7989
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:980
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:16990
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:453
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:279
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:15651
TActionVectorEntry::NewDescription
AnsiString NewDescription
Definition: TrainUnit.h:125
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:752
TTrain::NewTrainService
void NewTrainService(int Caller, bool NoLogFlag)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6602
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:9895
Timetable
@ Timetable
Definition: TrainUnit.h:61
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:16030
StartSequence
@ StartSequence
Definition: TrainUnit.h:78
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:7200
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:810
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:11278
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:785
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:133
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:18751
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:115
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:802
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:274
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:21519
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:83
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:443
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:770
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:266
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:808
Terminate
@ Terminate
Definition: TrainUnit.h:52
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:816
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
< map of coupled continuations
Definition: TrackUnit.h:799
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:491
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:149
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:505
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:375
Running
@ Running
Definition: TrainUnit.h:89
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:288
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3669
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:899
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:14973
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:16503
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:17266
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:463
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11498
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:727
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:239
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:858
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:431
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2577
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:19762
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:287
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0 for train failures, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:874
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1650
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:10376
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:9948
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:7037
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:868
TTrain::GetNewServiceDepartureInfo
AnsiString GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr, bool TimetableTime)
called during FloatingLabelNextString to find the next service departure time & next location (last b...
Definition: TrainUnit.cpp:7823
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:997
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:128
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:520
TUtilities::DelayMode
TDelayMode DelayMode
specifies whether no delays or minor, moderate or major random delays are to be applied (added at v2....
Definition: Utilities.h:117
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:9959
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:1014
TTrain::ExitPair
THVShortPair ExitPair
H & V coordinates of the exit element related to TimeToExit, new for multiplayer.
Definition: TrainUnit.h:477
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1737
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:816
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:12542
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3645
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3345
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:471
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
new v2.2.0 (DistanceToExit added for multiplayer), calcs distances to red signal & exit,...
Definition: TrainUnit.cpp:21832
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:807
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:73
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:54
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1648
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:9352
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:3209
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:123
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:834
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:17112
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:269
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:991
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:16444
TDisplay
Definition: DisplayUnit.h:49
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:147
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:876
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:492
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4779
TimeCmdDescription
@ TimeCmdDescription
Definition: TrainUnit.h:68
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:281
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in Actions...
Definition: TrainUnit.cpp:10710
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:53
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:363
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:413
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:12642
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:830
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:97
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:9973
TTrackElement::Failed
bool Failed
New parameter added at v2.13.0 for failed points, signals & TSRs.
Definition: TrackUnit.h:141
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:155
TTrainController::SkippedTTEvents
int SkippedTTEvents
Definition: TrainUnit.h:853
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:906
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:451
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:750
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:160
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1718
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:216
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:130
TUtilities::MinorDelayCutoff
float MinorDelayCutoff
Definition: Utilities.h:50
TTimeToExitMultiMapEntry
std::pair< THVShortPair, TExitInfo > TTimeToExitMultiMapEntry
Definition: TrainUnit.h:117
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:996
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:230
TTrain::DelayedRandMins
double DelayedRandMins
the remaining random delay at any point in time for the train (added at v2.13.0)
Definition: TrainUnit.h:445
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:149
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:823
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:909
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:558
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:5265
TTrain::TRSTime
TDateTime TRSTime
Definition: TrainUnit.h:473
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route (this will be truncated...
Definition: TrackUnit.h:1654
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:107
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1664
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:141
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:342
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:1020
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:715
Moderate
@ Moderate
Definition: Utilities.h:38
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:713
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:301
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:10360
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:897
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:839
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:359
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:17279
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:769
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:985
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:153
Major
@ Major
Definition: Utilities.h:38
Points
@ Points
Definition: TrackUnit.h:66
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:12052
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:743
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:399
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:833
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:15550
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:397
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:816
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:856
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:54
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3517
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:1004
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:407
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:268
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:509
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all signals appropriately. Also called when a new train is added a...
Definition: TrackUnit.cpp:17430
Continuation
@ Continuation
Definition: TrackUnit.h:66
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:802
TTrain::ActualArrivalTime
TDateTime ActualArrivalTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:473
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7747
GraphicUnit.h
PerfLogUnit.h
TUtilities::MajorDelayFactor
float MajorDelayFactor
Definition: Utilities.h:55
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:767
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:849
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:483
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:11127
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:55
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:369
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:21027
TTrack::TInfrastructureFailureEntry::FailureTime
TDateTime FailureTime
Definition: TrackUnit.h:715
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3303
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:902
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:54
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:783
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:905
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:1018
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:10741
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:88
TTrain::FollowOnServiceRef
AnsiString FollowOnServiceRef
Definition: TrainUnit.h:332
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:489
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:682
Minor
@ Minor
Definition: Utilities.h:38
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:741
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6865
TActionVectorEntry::ExitList
TNumList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:139
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:851
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:496
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:268
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:180
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrainDataEntry::ExplicitDescription
bool ExplicitDescription
true if a description is given for the train, if only headcode given for a follow-on service then fal...
Definition: TrainUnit.h:212
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:516
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:54
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:1001
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:20431
TTrain::RevisedStoppedAtLoc
bool RevisedStoppedAtLoc() const
Definition: TrainUnit.h:522
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:903
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:993
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:11136
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:318
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:285
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:709
TTrainController::SingleServiceOutput
void SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
Outputs the single service vector for train direction analysis purposes in timetable conflict analysi...
Definition: TrainUnit.cpp:20168
TTrack::TInfrastructureFailureEntry::RepairTime
TDateTime RepairTime
Definition: TrackUnit.h:716
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:724
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:9315
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:66
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:802
TTrain::NextTrainID
static int NextTrainID
< km/h
Definition: TrainUnit.h:327
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:769
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:188
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:83
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:726
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:988
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:19536
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:19492
TTrain::CumulativeDelayedRandMinsOneTrain
double CumulativeDelayedRandMinsOneTrain
the running total of all random delays including knock-on delays for a single train,...
Definition: TrainUnit.h:449
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:806
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:125
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:285
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
Definition: TrackUnit.h:155
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:909
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:340
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:51
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:724
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:373
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:843
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:1025
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:83
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:10327
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:16391
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:8039
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:18764
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6922
TTrain::AValue
double AValue
< only true when a train has become a follow-on service early and the follow-on service normally pass...
Definition: TrainUnit.h:423
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5710
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:20312
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:10345
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:149
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:14980
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:349
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:17072
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:186
TUtilities::MaxRandomRepairTime
int MaxRandomRepairTime
Definition: Utilities.h:71
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:155
NoLocation
@ NoLocation
Definition: TrainUnit.h:73
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool SetDataAndCheckLocations, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:15101
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:936
TTrainController::MinsToAnsiTime
AnsiString MinsToAnsiTime(int Input)
converts an integer minute value to string "HH:MM" added at v2.15.0
Definition: TrainUnit.cpp:16476
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:395
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:220
NoMode
@ NoMode
Definition: TrainUnit.h:61
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:845
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:12990
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:100
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:773
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:816
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:67
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:177
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:200
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:866
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:837
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:814
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:522
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5349
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:491
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:10729
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:984
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:143
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:300
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:44
TTrain::TrainSkippedEvents
int TrainSkippedEvents
stores the pointer increment from the current action in ActionVector for skipped actions when a depar...
Definition: TrainUnit.h:383
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:785
TExitInfo::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:109
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable - one entry per timetable service entry (the object i...
Definition: TrainUnit.h:243
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:16064
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:294
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7486
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:457
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:711
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1658
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:145
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:365
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:775
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:978
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:1021
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:771
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:3267
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
Definition: TrainUnit.h:125
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:147
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:417
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:105
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:1013
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:125
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6972
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4974
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:744
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:151
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:409
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:862
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:816
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:976
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6510
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:728
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:67
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:53
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:774
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:21473
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
THVShortPair
std::pair< int, int > THVShortPair
Definition: InterfaceUnit.h:82
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:740
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:67
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:829
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:1007
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:880
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:726
TActionVectorEntry::SignallerControl
bool SignallerControl
< string values for timetabled event entries, null on creation
Definition: TrainUnit.h:129
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:5254
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:441
TUtilities::LastDelayTTClockTime
double LastDelayTTClockTime
Clock time at which the latest delay for any train occurred. Used to prevent new delays within 5 minu...
Definition: Utilities.h:85
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:151
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:730
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:344
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:755
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:732
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:68
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:842
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:3191
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag = 1, top = 2, top rh diag = 3,...
Definition: TrackUnit.h:90
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:650
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1074
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:998
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:20596
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6893
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
Definition: TrackUnit.h:155
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3687
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:131
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2779
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:303
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:190
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:210
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:7231
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:347
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:76
Repeat
@ Repeat
Definition: TrainUnit.h:68
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:966
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:14989
FinishSequence
@ FinishSequence
Definition: TrainUnit.h:78
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:677
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:75
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:816
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:161
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:67
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:150
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:492
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:1000
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:749
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:891
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:251
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:6055
TAllRoutes::TLockedRouteClass::RearTrackVectorPosition
unsigned int RearTrackVectorPosition
the TrackVector position of the rearmost element selected for truncation (this will be truncated)
Definition: TrackUnit.h:1652
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:20338
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:145
Signaller
@ Signaller
Definition: TrainUnit.h:61
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:502
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:907
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:184
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:895
TTrain::TreatPassAsTimeLocDeparture
bool TreatPassAsTimeLocDeparture
< indicates failure
Definition: TrainUnit.h:421
Bridge
@ Bridge
Definition: TrackUnit.h:66
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:979
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:283
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:373
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:77
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:433
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:78
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:528
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:37
Buffers
@ Buffers
Definition: TrackUnit.h:66
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:135
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:297
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:125